// 初始化 Socket.IO 连接
const socket = io();

// 全局状态管理器 - 保持各模块状态独立
const globalStateManager = {
    // 各模块的状态存储
    moduleStates: {
        download: {
            // 下载模式状态
            downloadMode: 'single',
            batchUrls: [],
            batchVideoInfos: [],
            searchResults: [],
            selectedVideos: [],
            currentSearchContext: null,
            searchPagination: {
                currentPage: 1,
                totalPages: 1,
                itemsPerPage: 10,
                totalItems: 0
            },
            // UI状态
            downloadProgress: {},
            batchProgress: {},
            downloadLogs: [],
            isDownloading: false,
            downloadQueue: []
        },
        videoProcessing: {
            // 视频处理状态
            workflow: [],
            currentWorkflowId: null,
            availableFiles: [],
            processingLogs: [],
            isProcessing: false,
            processingProgress: 0,
            currentOperation: null,
            outputs: []
        },
        fileManagement: {
            // 文件管理状态
            currentFileMode: 'downloads',
            downloadsList: [],
            outputsList: [],
            backgroundsList: [],
            selectedFiles: [],
            sortBy: 'date',
            sortOrder: 'desc',
            filterType: 'all'
        }
    },
    
    // 当前活动模块
    currentModule: 'download',
    
    // 初始化状态管理器
    init() {
        this.loadStatesFromStorage();
        this.setupStateAutoSave();
        console.log('全局状态管理器已初始化');
    },
    
    // 从本地存储加载状态
    loadStatesFromStorage() {
        try {
            const savedStates = localStorage.getItem('globalModuleStates');
            if (savedStates) {
                const parsed = JSON.parse(savedStates);
                // 合并保存的状态，保留默认值
                this.moduleStates = this.deepMerge(this.moduleStates, parsed);
            }
        } catch (error) {
            console.error('加载模块状态失败:', error);
        }
    },
    
    // 保存状态到本地存储
    saveStatesToStorage() {
        try {
            localStorage.setItem('globalModuleStates', JSON.stringify(this.moduleStates));
        } catch (error) {
            console.error('保存模块状态失败:', error);
        }
    },
    
    // 设置自动保存
    setupStateAutoSave() {
        // 每30秒自动保存一次
        setInterval(() => {
            this.saveStatesToStorage();
        }, 30000);
        
        // 页面卸载时保存
        window.addEventListener('beforeunload', () => {
            this.saveStatesToStorage();
        });
    },
    
    // 深度合并对象
    deepMerge(target, source) {
        const result = { ...target };
        for (const key in source) {
            if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
                result[key] = this.deepMerge(target[key] || {}, source[key]);
            } else {
                result[key] = source[key];
            }
        }
        return result;
    },
    
    // 获取模块状态
    getModuleState(moduleName) {
        return this.moduleStates[moduleName] || {};
    },
    
    // 更新模块状态
    updateModuleState(moduleName, stateUpdate) {
        if (!this.moduleStates[moduleName]) {
            this.moduleStates[moduleName] = {};
        }
        
        // 深度合并状态更新
        this.moduleStates[moduleName] = this.deepMerge(this.moduleStates[moduleName], stateUpdate);
        
        // 立即保存重要状态变更
        if (this.isImportantStateChange(moduleName, stateUpdate)) {
            this.saveStatesToStorage();
        }
    },
    
    // 判断是否为重要状态变更（需要立即保存）
    isImportantStateChange(moduleName, stateUpdate) {
        const importantKeys = {
            download: ['downloadMode', 'batchUrls', 'searchResults', 'selectedVideos', 'isDownloading'],
            videoProcessing: ['workflow', 'isProcessing', 'processingProgress', 'outputs'],
            fileManagement: ['currentFileMode', 'selectedFiles']
        };
        
        const moduleImportantKeys = importantKeys[moduleName] || [];
        return Object.keys(stateUpdate).some(key => moduleImportantKeys.includes(key));
    },
    
    // 切换模块时保存当前状态并恢复目标模块状态
    switchModule(fromModule, toModule) {
        console.log(`状态管理器：从 ${fromModule} 切换到 ${toModule}`);
        
        // 保存当前模块状态
        this.saveCurrentModuleState(fromModule);
        
        // 更新当前模块
        this.currentModule = toModule;
        
        // 恢复目标模块状态
        this.restoreModuleState(toModule);
        
        // 立即保存状态切换
        this.saveStatesToStorage();
    },
    
    // 保存当前模块状态
    saveCurrentModuleState(moduleName) {
        switch (moduleName) {
            case 'download':
                this.saveDownloadModuleState();
                break;
            case 'videoProcessing':
                this.saveVideoProcessingState();
                break;
            case 'fileManagement':
                this.saveFileManagementState();
                break;
        }
    },
    
    // 恢复模块状态
    restoreModuleState(moduleName) {
        switch (moduleName) {
            case 'download':
                this.restoreDownloadModuleState();
                break;
            case 'videoProcessing':
                this.restoreVideoProcessingState();
                break;
            case 'fileManagement':
                this.restoreFileManagementState();
                break;
        }
    },
    
    // 保存下载模块状态
    saveDownloadModuleState() {
        const downloadState = {
            downloadMode: appState.downloadMode,
            batchUrls: appState.batchUrls || [],
            batchVideoInfos: appState.batchVideoInfos || [],
            searchResults: appState.searchResults || [],
            selectedVideos: appState.selectedVideos || [],
            currentSearchContext: appState.currentSearchContext,
            searchPagination: appState.searchPagination,
            // 保存UI状态
            downloadLogs: this.getDownloadLogs(),
            isDownloading: this.isDownloadInProgress(),
            downloadQueue: this.getCurrentDownloadQueue()
        };
        
        this.updateModuleState('download', downloadState);
    },
    
    // 恢复下载模块状态
    restoreDownloadModuleState() {
        const state = this.getModuleState('download');
        
        // 恢复appState
        if (state.downloadMode) appState.downloadMode = state.downloadMode;
        if (state.batchUrls) appState.batchUrls = state.batchUrls;
        if (state.batchVideoInfos) appState.batchVideoInfos = state.batchVideoInfos;
        if (state.searchResults) appState.searchResults = state.searchResults;
        if (state.selectedVideos) appState.selectedVideos = state.selectedVideos;
        if (state.currentSearchContext) appState.currentSearchContext = state.currentSearchContext;
        if (state.searchPagination) appState.searchPagination = state.searchPagination;
        
        // 恢复UI状态
        this.restoreDownloadUI(state);
    },
    
    // 保存视频处理状态
    saveVideoProcessingState() {
        if (typeof videoProcessing !== 'undefined') {
            const processingState = {
                workflow: videoProcessing.workflow || [],
                currentWorkflowId: videoProcessing.currentWorkflowId,
                processingLogs: this.getProcessingLogs(),
                isProcessing: this.isVideoProcessingInProgress(),
                processingProgress: this.getProcessingProgress(),
                currentOperation: this.getCurrentOperation(),
                outputs: videoProcessing.outputs || []
            };
            
            this.updateModuleState('videoProcessing', processingState);
        }
    },
    
    // 恢复视频处理状态
    restoreVideoProcessingState() {
        const state = this.getModuleState('videoProcessing');
        
        if (typeof videoProcessing !== 'undefined') {
            // 恢复工作流
            if (state.workflow) {
                videoProcessing.workflow = state.workflow;
                this.restoreWorkflowDisplay();
            }
            
            if (state.currentWorkflowId) {
                videoProcessing.currentWorkflowId = state.currentWorkflowId;
            }
            
            if (state.outputs) {
                videoProcessing.outputs = state.outputs;
            }
            
            // 恢复处理日志和进度
            this.restoreProcessingUI(state);
        }
    },
    
    // 保存文件管理状态
    saveFileManagementState() {
        const fileManagementState = {
            currentFileMode: this.getCurrentFileMode(),
            selectedFiles: this.getSelectedFiles(),
            sortBy: this.getCurrentSortBy(),
            sortOrder: this.getCurrentSortOrder(),
            filterType: this.getCurrentFilterType()
        };
        
        this.updateModuleState('fileManagement', fileManagementState);
    },
    
    // 恢复文件管理状态
    restoreFileManagementState() {
        const state = this.getModuleState('fileManagement');
        
        // 恢复文件模式
        if (state.currentFileMode) {
            this.restoreFileMode(state.currentFileMode);
        }
        
        // 恢复选中文件
        if (state.selectedFiles) {
            this.restoreSelectedFiles(state.selectedFiles);
        }
        
        // 恢复排序和过滤设置
        this.restoreFileManagementUI(state);
    },
    
    // 辅助方法：获取下载日志
    getDownloadLogs() {
        const logContainer = document.getElementById('downloadLog');
        if (logContainer) {
            return Array.from(logContainer.children).map(child => ({
                content: child.innerHTML,
                className: child.className,
                timestamp: child.dataset.timestamp || Date.now()
            }));
        }
        return [];
    },
    
    // 辅助方法：检查是否有下载进行中
    isDownloadInProgress() {
        return document.querySelector('.download-progress:not([style*="display: none"])') !== null;
    },
    
    // 辅助方法：获取当前下载队列
    getCurrentDownloadQueue() {
        // 从全局变量或DOM中获取下载队列信息
        return window.batchQueue || [];
    },
    
    // 辅助方法：获取处理日志
    getProcessingLogs() {
        const logContainer = document.getElementById('processingLogContainer');
        if (logContainer) {
            return Array.from(logContainer.children).map(child => ({
                content: child.innerHTML,
                className: child.className,
                timestamp: child.dataset.timestamp || Date.now()
            }));
        }
        return [];
    },
    
    // 辅助方法：检查视频处理是否进行中
    isVideoProcessingInProgress() {
        const progressSection = document.getElementById('processingProgress');
        return progressSection && progressSection.style.display !== 'none';
    },
    
    // 辅助方法：获取处理进度
    getProcessingProgress() {
        const progressBar = document.querySelector('#processingProgress .progress-bar');
        return progressBar ? parseInt(progressBar.style.width) || 0 : 0;
    },
    
    // 辅助方法：获取当前操作
    getCurrentOperation() {
        const operationElement = document.querySelector('#processingProgress .current-operation');
        return operationElement ? operationElement.textContent : null;
    },
    
    // 辅助方法：获取当前文件模式
    getCurrentFileMode() {
        const checkedRadio = document.querySelector('input[name="fileMode"]:checked');
        return checkedRadio ? checkedRadio.value : 'downloads';
    },
    
    // 辅助方法：获取选中文件
    getSelectedFiles() {
        const selectedElements = document.querySelectorAll('.file-item.selected');
        return Array.from(selectedElements).map(el => el.dataset.filename || el.textContent);
    },
    
    // 辅助方法：获取当前排序方式
    getCurrentSortBy() {
        const sortSelect = document.getElementById('sortBy');
        return sortSelect ? sortSelect.value : 'date';
    },
    
    // 辅助方法：获取当前排序顺序
    getCurrentSortOrder() {
        const orderSelect = document.getElementById('sortOrder');
        return orderSelect ? orderSelect.value : 'desc';
    },
    
    // 辅助方法：获取当前过滤类型
    getCurrentFilterType() {
        const filterSelect = document.getElementById('filterType');
        return filterSelect ? filterSelect.value : 'all';
    },
    
    // 恢复下载UI状态
    restoreDownloadUI(state) {
        // 恢复下载模式选择
        if (state.downloadMode) {
            const modeRadio = document.querySelector(`input[name="downloadMode"][value="${state.downloadMode}"]`);
            if (modeRadio) {
                modeRadio.checked = true;
                // 触发模式切换事件
                modeRadio.dispatchEvent(new Event('change'));
            }
        }
        
        // 恢复下载日志
        if (state.downloadLogs && state.downloadLogs.length > 0) {
            const logContainer = document.getElementById('downloadLog');
            if (logContainer) {
                logContainer.innerHTML = '';
                state.downloadLogs.forEach(log => {
                    const logElement = document.createElement('div');
                    logElement.className = log.className;
                    logElement.innerHTML = log.content;
                    logElement.dataset.timestamp = log.timestamp;
                    logContainer.appendChild(logElement);
                });
                // 滚动到底部
                logContainer.scrollTop = logContainer.scrollHeight;
            }
        }
        
        // 恢复下载进度状态
        if (state.isDownloading) {
            const progressSection = document.getElementById('progressSection');
            if (progressSection) {
                progressSection.style.display = 'block';
            }
        }
        
        // 恢复搜索结果显示
        if (state.searchResults && state.searchResults.length > 0) {
            this.restoreSearchResults(state);
        }
        
        // 恢复批量下载信息
        if (state.batchVideoInfos && state.batchVideoInfos.length > 0) {
            this.restoreBatchVideoInfos(state.batchVideoInfos);
        }
    },
    
    // 恢复工作流显示
    restoreWorkflowDisplay() {
        if (typeof videoProcessing !== 'undefined' && videoProcessing.updateWorkflowDisplay) {
            videoProcessing.updateWorkflowDisplay();
        }
    },
    
    // 恢复处理UI状态
    restoreProcessingUI(state) {
        // 恢复处理日志
        if (state.processingLogs && state.processingLogs.length > 0) {
            const logContainer = document.getElementById('processingLogContainer');
            if (logContainer) {
                logContainer.innerHTML = '';
                state.processingLogs.forEach(log => {
                    const logElement = document.createElement('div');
                    logElement.className = log.className;
                    logElement.innerHTML = log.content;
                    logElement.dataset.timestamp = log.timestamp;
                    logContainer.appendChild(logElement);
                });
                // 滚动到底部
                logContainer.scrollTop = logContainer.scrollHeight;
            }
        }
        
        // 恢复处理进度
        if (state.isProcessing && state.processingProgress !== undefined) {
            const progressSection = document.getElementById('processingProgress');
            if (progressSection) {
                progressSection.style.display = 'block';
                const progressBar = progressSection.querySelector('.progress-bar');
                if (progressBar) {
                    progressBar.style.width = state.processingProgress + '%';
                }
            }
        }
        
        // 恢复当前操作显示
        if (state.currentOperation) {
            const operationElement = document.querySelector('#processingProgress .current-operation');
            if (operationElement) {
                operationElement.textContent = state.currentOperation;
            }
        }
    },
    
    // 恢复文件模式
    restoreFileMode(fileMode) {
        const fileModeRadio = document.querySelector(`input[name="fileMode"][value="${fileMode}"]`);
        if (fileModeRadio) {
            fileModeRadio.checked = true;
            // 触发模式切换
            if (typeof appMode !== 'undefined' && appMode.switchFileMode) {
                appMode.switchFileMode(fileMode);
            }
        }
    },
    
    // 恢复选中文件
    restoreSelectedFiles(selectedFiles) {
        setTimeout(() => {
            selectedFiles.forEach(filename => {
                const fileElement = document.querySelector(`[data-filename="${filename}"]`);
                if (fileElement) {
                    fileElement.classList.add('selected');
                }
            });
        }, 100);
    },
    
    // 恢复文件管理UI
    restoreFileManagementUI(state) {
        // 恢复排序设置
        if (state.sortBy) {
            const sortSelect = document.getElementById('sortBy');
            if (sortSelect) sortSelect.value = state.sortBy;
        }
        
        if (state.sortOrder) {
            const orderSelect = document.getElementById('sortOrder');
            if (orderSelect) orderSelect.value = state.sortOrder;
        }
        
        // 恢复过滤设置
        if (state.filterType) {
            const filterSelect = document.getElementById('filterType');
            if (filterSelect) filterSelect.value = state.filterType;
        }
    },
    
    // 恢复搜索结果
    restoreSearchResults(state) {
        if (typeof displaySearchResults === 'function') {
            displaySearchResults(state.searchResults, state.currentSearchContext);
        }
    },
    
    // 恢复批量视频信息
    restoreBatchVideoInfos(batchVideoInfos) {
        if (typeof displayBatchVideoInfos === 'function') {
            displayBatchVideoInfos(batchVideoInfos);
        }
    }
}

// ==================== 记录链接功能 ====================

// 记录链接管理器
const savedLinksManager = {
    init() {
        this.bindEvents();
        this.loadSavedLinks();
    },

    bindEvents() {
        // 保存链接按钮
        if (elements.saveLinksBtn) {
            elements.saveLinksBtn.addEventListener('click', () => this.saveLinks());
        }

        // 视图模式切换
        if (elements.viewModeRadios) {
            elements.viewModeRadios.forEach(radio => {
                radio.addEventListener('change', (e) => this.handleViewModeChange(e.target.value));
            });
        }

        // 标签过滤器清除按钮
        if (elements.clearTagFilterBtn) {
            elements.clearTagFilterBtn.addEventListener('click', () => this.clearTagFilter());
        }

        // 全选复选框
        if (elements.selectAllSaved) {
            elements.selectAllSaved.addEventListener('change', (e) => this.handleSelectAllSaved(e.target.checked));
        }

        // 下载选中按钮
        if (elements.downloadSelectedSavedBtn) {
            elements.downloadSelectedSavedBtn.addEventListener('click', () => this.downloadSelectedSaved());
        }

        // 删除选中按钮
        if (elements.deleteSelectedSavedBtn) {
            elements.deleteSelectedSavedBtn.addEventListener('click', () => this.deleteSelectedSaved());
        }
    },

    // 保存链接
    async saveLinks() {
        const linksText = elements.linksToSave.value.trim();
        const tag = elements.linkTag.value.trim();

        if (!linksText) {
            showToast('请输入要保存的链接', 'warning');
            return;
        }

        if (!tag) {
            showToast('请输入标签', 'warning');
            return;
        }

        // 解析链接
        const urls = linksText.split('\n')
            .map(line => line.trim())
            .filter(line => line && line.startsWith('http'));

        if (urls.length === 0) {
            showToast('没有找到有效的链接', 'warning');
            return;
        }

        elements.saveLinksBtn.disabled = true;
        elements.saveLinksBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 保存中...';

        try {
            const response = await fetch('/api/save-links', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ urls, tag })
            });

            const result = await response.json();

            if (result.success) {
                showToast(`成功保存 ${result.savedCount} 个链接`, 'success');
                
                // 清空输入框
                elements.linksToSave.value = '';
                elements.linkTag.value = '';
                
                // 重新加载保存的链接
                await this.loadSavedLinks();
                
                // 更新标签列表
                this.updateTagsList();
            } else {
                showToast('保存失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('保存链接失败:', error);
            showToast('保存链接失败: ' + error.message, 'error');
        } finally {
            elements.saveLinksBtn.disabled = false;
            elements.saveLinksBtn.innerHTML = '<i class="fas fa-save"></i> 保存链接';
        }
    },

    // 加载保存的链接
    async loadSavedLinks() {
        try {
            const response = await fetch('/api/saved-links');
            const result = await response.json();

            if (result.success) {
                appState.savedLinks = result.links || [];
                appState.availableTags = result.tags || [];
                
                // 更新元数据状态信息
                if (result.metadataStatus) {
                    this.updateMetadataStatus(result.metadataStatus);
                }
                
                this.updateDisplay();
                this.updateTagsList();
                
                // 如果有待处理的元数据，启动定期刷新
                if (result.metadataStatus && result.metadataStatus.pending > 0) {
                    this.startMetadataPolling();
                }
            } else {
                console.error('加载保存的链接失败:', result.error);
                appState.savedLinks = [];
                appState.availableTags = [];
                this.updateDisplay();
            }
        } catch (error) {
            console.error('加载保存的链接失败:', error);
            appState.savedLinks = [];
            appState.availableTags = [];
            this.updateDisplay();
        }
    },

    // 更新元数据状态显示
    updateMetadataStatus(status) {
        const statusContainer = document.getElementById('metadata-status');
        if (!statusContainer) return;

        if (status.pending > 0 || status.isProcessing) {
            statusContainer.innerHTML = `
                <div class="metadata-status-info">
                    <i class="fas fa-info-circle"></i>
                    正在后台获取视频信息... 
                    (已完成: ${status.updated}/${status.total}, 
                    ${status.isProcessing ? '处理中' : '等待中'}: ${status.pending})
                </div>
            `;
            statusContainer.style.display = 'block';
        } else {
            statusContainer.style.display = 'none';
        }
    },

    // 开始元数据轮询
    startMetadataPolling() {
        if (this.metadataPollingInterval) {
            clearInterval(this.metadataPollingInterval);
        }

        this.metadataPollingInterval = setInterval(async () => {
            try {
                const response = await fetch('/api/metadata-status');
                const result = await response.json();

                if (result.success) {
                    this.updateMetadataStatus(result.status);
                    
                    // 如果没有待处理的项目，停止轮询并刷新链接列表
                    if (result.status.pending === 0 && !result.status.isProcessing) {
                        clearInterval(this.metadataPollingInterval);
                        this.metadataPollingInterval = null;
                        await this.loadSavedLinks(); // 重新加载以获取更新的信息
                    }
                }
            } catch (error) {
                console.error('获取元数据状态失败:', error);
            }
        }, 3000); // 每3秒检查一次
    },

    // 更新显示
    updateDisplay() {
        if (!elements.savedLinksContainer) return;

        let linksToShow = appState.savedLinks;

        // 应用标签过滤
        if (appState.currentTagFilter) {
            linksToShow = linksToShow.filter(link => link.tag === appState.currentTagFilter);
        }

        if (linksToShow.length === 0) {
            elements.savedLinksContainer.innerHTML = `
                <div class="empty-state">
                    <i class="fas fa-bookmark"></i>
                    <p>${appState.currentTagFilter ? '该标签下没有保存的链接' : '还没有保存任何链接'}</p>
                    <p>在上方输入框中添加视频链接并保存</p>
                </div>
            `;
            return;
        }

        if (appState.currentViewMode === 'tags') {
            this.renderTagsView(linksToShow);
        } else {
            this.renderListView(linksToShow);
        }

        this.updateSelectedCount();
    },

    // 渲染列表视图
    renderListView(links) {
        const html = links.map(link => this.createSavedLinkItem(link)).join('');
        elements.savedLinksContainer.innerHTML = html;
    },

    // 渲染标签视图
    renderTagsView(links) {
        const groupedByTag = {};
        links.forEach(link => {
            if (!groupedByTag[link.tag]) {
                groupedByTag[link.tag] = [];
            }
            groupedByTag[link.tag].push(link);
        });

        const html = Object.entries(groupedByTag).map(([tag, tagLinks]) => `
            <div class="tag-group">
                <div class="tag-group-header">
                    <h4><i class="fas fa-tag"></i> ${tag} (${tagLinks.length})</h4>
                </div>
                <div class="tag-group-content">
                    ${tagLinks.map(link => this.createSavedLinkItem(link)).join('')}
                </div>
            </div>
        `).join('');

        elements.savedLinksContainer.innerHTML = html;
    },

    // 创建保存的链接项
    createSavedLinkItem(link) {
        const isSelected = appState.selectedSavedLinks.includes(link.id);
        const isLoading = !link.metadataUpdated;
        const hasError = link.metadataError;
        
        return `
            <div class="saved-link-item ${isSelected ? 'selected' : ''} ${isLoading ? 'loading' : ''}" data-id="${link.id}">
                <div class="saved-link-checkbox">
                    <input type="checkbox" ${isSelected ? 'checked' : ''} 
                           onchange="savedLinksManager.handleLinkSelection(${link.id}, this.checked)">
                </div>
                <div class="saved-link-thumbnail">
                    ${link.thumbnail && !isLoading ? 
                        `<img src="${link.thumbnail}" alt="缩略图" onerror="this.style.display='none'">` : 
                        `<div style="display:flex;align-items:center;justify-content:center;height:100%;background:#f0f0f0;color:#999;">
                            ${isLoading ? '<i class="fas fa-spinner fa-spin"></i>' : '<i class="fas fa-video"></i>'}
                        </div>`
                    }
                </div>
                <div class="saved-link-info">
                    <div class="saved-link-title">
                        ${isLoading ? 
                            '<span class="loading-text"><i class="fas fa-spinner fa-spin"></i> 正在获取视频信息...</span>' : 
                            (hasError ? '<span class="error-text">获取信息失败</span>' : (link.title || '未知标题'))
                        }
                    </div>
                    <div class="saved-link-channel">
                        ${isLoading ? '正在获取...' : (hasError ? '获取失败' : (link.channel || '未知频道'))}
                    </div>
                    <div class="saved-link-meta">
                        ${!isLoading && !hasError && link.duration ? `<span><i class="fas fa-clock"></i> ${link.duration}</span>` : ''}
                        ${!isLoading && !hasError && link.viewCount ? `<span><i class="fas fa-eye"></i> ${link.viewCount}</span>` : ''}
                        <span><i class="fas fa-calendar"></i> ${new Date(link.savedAt).toLocaleDateString()}</span>
                        ${link.metadataUpdatedAt ? `<span title="信息更新时间"><i class="fas fa-sync"></i> ${new Date(link.metadataUpdatedAt).toLocaleString()}</span>` : ''}
                    </div>
                    <div class="saved-link-tag">
                        <i class="fas fa-tag"></i> ${link.tag}
                    </div>
                    <div class="saved-link-url">${link.url}</div>
                </div>
                <div class="saved-link-actions">
                    <button type="button" class="btn btn-small btn-primary" 
                            onclick="savedLinksManager.downloadSingleLink(${link.id})"
                            ${isLoading ? 'disabled' : ''}>
                        <i class="fas fa-download"></i> 下载
                    </button>
                    <button type="button" class="btn btn-small btn-danger" 
                            onclick="savedLinksManager.deleteSingleLink(${link.id})">
                        <i class="fas fa-trash"></i> 删除
                    </button>
                </div>
            </div>
        `;
    },

    // 处理链接选择
    handleLinkSelection(linkId, checked) {
        if (checked) {
            if (!appState.selectedSavedLinks.includes(linkId)) {
                appState.selectedSavedLinks.push(linkId);
            }
        } else {
            appState.selectedSavedLinks = appState.selectedSavedLinks.filter(id => id !== linkId);
        }

        this.updateSelectedCount();
        this.updateLinkItemSelection(linkId, checked);
    },

    // 更新链接项选择状态
    updateLinkItemSelection(linkId, selected) {
        const linkItem = document.querySelector(`[data-id="${linkId}"]`);
        if (linkItem) {
            if (selected) {
                linkItem.classList.add('selected');
            } else {
                linkItem.classList.remove('selected');
            }
        }
    },

    // 处理全选
    handleSelectAllSaved(checked) {
        let linksToShow = appState.savedLinks;
        
        // 应用标签过滤
        if (appState.currentTagFilter) {
            linksToShow = linksToShow.filter(link => link.tag === appState.currentTagFilter);
        }

        if (checked) {
            appState.selectedSavedLinks = linksToShow.map(link => link.id);
        } else {
            appState.selectedSavedLinks = [];
        }

        // 更新所有复选框
        document.querySelectorAll('.saved-link-checkbox input[type="checkbox"]').forEach(checkbox => {
            checkbox.checked = checked;
        });

        // 更新所有项的选择状态
        document.querySelectorAll('.saved-link-item').forEach(item => {
            if (checked) {
                item.classList.add('selected');
            } else {
                item.classList.remove('selected');
            }
        });

        this.updateSelectedCount();
    },

    // 更新选中数量
    updateSelectedCount() {
        const count = appState.selectedSavedLinks.length;
        if (elements.selectedSavedCount) {
            elements.selectedSavedCount.textContent = count;
        }

        // 更新按钮状态
        if (elements.downloadSelectedSavedBtn) {
            elements.downloadSelectedSavedBtn.disabled = count === 0;
        }
        if (elements.deleteSelectedSavedBtn) {
            elements.deleteSelectedSavedBtn.disabled = count === 0;
        }

        // 更新全选复选框状态
        if (elements.selectAllSaved) {
            let visibleLinks = appState.savedLinks;
            if (appState.currentTagFilter) {
                visibleLinks = visibleLinks.filter(link => link.tag === appState.currentTagFilter);
            }
            
            if (visibleLinks.length === 0) {
                elements.selectAllSaved.indeterminate = false;
                elements.selectAllSaved.checked = false;
            } else if (count === 0) {
                elements.selectAllSaved.indeterminate = false;
                elements.selectAllSaved.checked = false;
            } else if (count === visibleLinks.length) {
                elements.selectAllSaved.indeterminate = false;
                elements.selectAllSaved.checked = true;
            } else {
                elements.selectAllSaved.indeterminate = true;
                elements.selectAllSaved.checked = false;
            }
        }
    },

    // 处理视图模式切换
    handleViewModeChange(mode) {
        appState.currentViewMode = mode;
        
        // 显示/隐藏标签过滤器
        if (elements.tagFilter) {
            elements.tagFilter.style.display = mode === 'tags' ? 'block' : 'none';
        }

        this.updateDisplay();
    },

    // 更新标签列表
    updateTagsList() {
        // 更新标签过滤器
        if (elements.tagList) {
            const tagCounts = {};
            appState.savedLinks.forEach(link => {
                tagCounts[link.tag] = (tagCounts[link.tag] || 0) + 1;
            });

            const html = Object.entries(tagCounts).map(([tag, count]) => `
                <div class="tag-item ${appState.currentTagFilter === tag ? 'active' : ''}" 
                     onclick="savedLinksManager.setTagFilter('${tag}')">
                    <span>${tag}</span>
                    <span class="tag-count">${count}</span>
                </div>
            `).join('');

            elements.tagList.innerHTML = html;
        }

        // 更新标签输入的datalist
        if (elements.existingTags) {
            const html = appState.availableTags.map(tag => `<option value="${tag}">`).join('');
            elements.existingTags.innerHTML = html;
        }
    },

    // 设置标签过滤器
    setTagFilter(tag) {
        if (appState.currentTagFilter === tag) {
            // 如果点击的是当前过滤标签，则清除过滤
            this.clearTagFilter();
        } else {
            appState.currentTagFilter = tag;
            appState.selectedSavedLinks = []; // 清除选择
            this.updateDisplay();
            this.updateTagsList();
        }
    },

    // 清除标签过滤器
    clearTagFilter() {
        appState.currentTagFilter = null;
        appState.selectedSavedLinks = []; // 清除选择
        this.updateDisplay();
        this.updateTagsList();
    },

    // 下载选中的链接
    async downloadSelectedSaved() {
        if (appState.selectedSavedLinks.length === 0) {
            showToast('请选择要下载的链接', 'warning');
            return;
        }

        const selectedLinks = appState.savedLinks.filter(link => 
            appState.selectedSavedLinks.includes(link.id)
        );

        const urls = selectedLinks.map(link => link.url);

        // 使用批量下载功能
        await this.downloadUrls(urls);
    },

    // 下载单个链接
    async downloadSingleLink(linkId) {
        const link = appState.savedLinks.find(l => l.id === linkId);
        if (!link) {
            showToast('链接不存在', 'error');
            return;
        }

        await this.downloadUrls([link.url]);
    },

    // 下载URL列表
    async downloadUrls(urls) {
        // 切换到批量下载模式
        document.querySelector('input[name="downloadMode"][value="batch"]').checked = true;
        appState.downloadMode = 'batch';
        handleDownloadModeChange({ target: { value: 'batch' } });

        // 填充URL到批量下载文本框
        elements.batchUrlsTextarea.value = urls.join('\n');
        handleBatchUrlsInput();

        // 如果当前有选中的标签过滤器，将其作为关键字填入批量下载
        if (appState.currentTagFilter && elements.batchKeywordInput) {
            elements.batchKeywordInput.value = appState.currentTagFilter;
        }

        // 显示成功消息
        const tagMessage = appState.currentTagFilter ? `，并将标签"${appState.currentTagFilter}"设为关键字` : '';
        showToast(`已将 ${urls.length} 个链接添加到批量下载${tagMessage}`, 'success');
    },

    // 删除选中的链接
    async deleteSelectedSaved() {
        if (appState.selectedSavedLinks.length === 0) {
            showToast('请选择要删除的链接', 'warning');
            return;
        }

        const confirmed = await showCustomConfirm(
            '确认删除',
            `确定要删除选中的 ${appState.selectedSavedLinks.length} 个链接吗？此操作不可撤销。`
        );

        if (!confirmed) return;

        try {
            const response = await fetch('/api/delete-saved-links', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ linkIds: appState.selectedSavedLinks })
            });

            const result = await response.json();

            if (result.success) {
                showToast(`成功删除 ${result.deletedCount} 个链接`, 'success');
                appState.selectedSavedLinks = [];
                await this.loadSavedLinks();
            } else {
                showToast('删除失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('删除链接失败:', error);
            showToast('删除链接失败: ' + error.message, 'error');
        }
    },

    // 删除单个链接
    async deleteSingleLink(linkId) {
        const confirmed = await showCustomConfirm(
            '确认删除',
            '确定要删除这个链接吗？此操作不可撤销。'
        );

        if (!confirmed) return;

        try {
            const response = await fetch('/api/delete-saved-links', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ linkIds: [linkId] })
            });

            const result = await response.json();

            if (result.success) {
                showToast('链接删除成功', 'success');
                // 从选中列表中移除
                appState.selectedSavedLinks = appState.selectedSavedLinks.filter(id => id !== linkId);
                await this.loadSavedLinks();
            } else {
                showToast('删除失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('删除链接失败:', error);
            showToast('删除链接失败: ' + error.message, 'error');
        }
    }
};

// 加载保存的链接（供外部调用）
function loadSavedLinks() {
    savedLinksManager.loadSavedLinks();
};

// 全局变量
let isDownloading = false;
let isBatchDownloading = false;
let batchQueue = [];
let currentBatchDownloads = [];
let batchResults = [];
let maxConcurrentDownloads = 2;

// DOM 元素
const elements = {
    // 原有元素
    statusBar: document.getElementById('statusBar'),
    ytdlpStatus: document.getElementById('ytdlpStatus'),
    videoUrl: document.getElementById('videoUrl'),
    qualitySelect: document.getElementById('quality'),
    audioFormatSelect: document.getElementById('audioFormat'),
    audioOnlyCheckbox: document.getElementById('audioOnly'),
    playlistCheckbox: document.getElementById('playlist'),
    subtitlesCheckbox: document.getElementById('subtitles'),
    subtitleLang: document.getElementById('subtitleLang'),
    subtitleOptions: document.getElementById('subtitleOptions'),
    saveAsDirectorySelect: document.getElementById('saveAsDirectory'),
    getInfoBtn: document.getElementById('getInfoBtn'),
    downloadBtn: document.getElementById('downloadBtn'),
    refreshBtn: document.getElementById('refreshBtn'),
    infoSection: document.getElementById('infoSection'),
    videoInfo: document.getElementById('videoInfo'),
    progressSection: document.getElementById('progressSection'),
    progressFill: document.getElementById('progressFill'),
    progressText: document.getElementById('progressText'),
    logContainer: document.getElementById('logContainer'),
    downloadsList: document.getElementById('downloadsList'),
    
    // 批量下载元素
    downloadModeRadios: document.querySelectorAll('input[name="downloadMode"]'),
    singleDownloadSection: document.getElementById('singleDownload'),
    batchDownloadSection: document.getElementById('batchDownload'),
    batchUrlsTextarea: document.getElementById('batchUrls'),
    batchKeywordInput: document.getElementById('batchKeyword'),
    urlCountSpan: document.getElementById('urlCount'),
    validateUrlsBtn: document.getElementById('validateUrlsBtn'),
    clearUrlsBtn: document.getElementById('clearUrlsBtn'),
    enableConcurrentCheckbox: document.getElementById('batchConcurrent'),
    maxConcurrentSelect: document.getElementById('maxConcurrent'),
    batchGetInfoBtn: document.getElementById('batchInfoBtn'),
    batchDownloadBtn: document.getElementById('batchDownloadBtn'),
    stopBatchBtn: document.getElementById('stopBatchBtn'),
    batchInfoSection: document.getElementById('batchInfoSection'),
    batchSummary: document.querySelector('.batch-summary'),
    batchVideoList: document.getElementById('batchVideoInfo'),
    batchProgressSection: document.getElementById('batchProgressSection'),
    overallProgressBar: document.getElementById('overallProgressFill'),
    overallProgressText: document.getElementById('overallProgressText'),
    currentDownloadsContainer: document.getElementById('currentDownloads'),
    downloadQueueContainer: document.getElementById('downloadQueue'),
    batchLogContainer: document.getElementById('batchLogContainer'),
    
    // 搜索下载元素
    searchDownloadSection: document.getElementById('searchDownload'),
    searchKeyword: document.getElementById('searchKeyword'),
    searchType: document.getElementById('searchType'),
    videoType: document.getElementById('videoType'),
    searchBtn: document.getElementById('searchBtn'),
    maxResults: document.getElementById('maxResults'),
    sortBy: document.getElementById('sortBy'),
    searchResults: document.getElementById('searchResults'),
    searchResultsHeader: document.getElementById('searchResultsHeader'),
    selectAllCheckbox: document.getElementById('selectAll'),
    selectedCount: document.getElementById('selectedCount'),
    searchResultsList: document.getElementById('searchResultsList'),
    downloadSelectedBtn: document.getElementById('downloadSelectedBtn'),
    clearSearchBtn: document.getElementById('clearSearchBtn'),
    searchButtons: document.getElementById('searchButtons'),
    
    // 新增元素
    connectionStatus: document.getElementById('connectionStatus'),
    connectionText: document.getElementById('statusText'),
    searchConcurrentSelect: document.getElementById('searchConcurrent'),
    customConfirmModal: document.getElementById('customConfirmModal'),
    confirmTitle: document.getElementById('confirmTitle'),
    confirmMessage: document.getElementById('confirmMessage'),
    confirmCancelBtn: document.getElementById('confirmCancelBtn'),
    confirmOkBtn: document.getElementById('confirmOkBtn'),
    paginationContainer: document.getElementById('paginationControls'),
    prevPageBtn: document.getElementById('prevPageBtn'),
    nextPageBtn: document.getElementById('nextPageBtn'),
    currentPageSpan: document.getElementById('currentPage'),
    totalPagesSpan: document.getElementById('totalPages'),
    
    // 记录链接元素
    savedLinksSection: document.getElementById('savedLinks'),
    linksToSave: document.getElementById('linksToSave'),
    linkTag: document.getElementById('linkTag'),
    existingTags: document.getElementById('existingTags'),
    saveLinksBtn: document.getElementById('saveLinksBtn'),
    viewModeRadios: document.querySelectorAll('input[name="viewMode"]'),
    tagFilter: document.getElementById('tagFilter'),
    tagList: document.getElementById('tagList'),
    clearTagFilterBtn: document.getElementById('clearTagFilterBtn'),
    selectAllSaved: document.getElementById('selectAllSaved'),
    selectedSavedCount: document.getElementById('selectedSavedCount'),
    downloadSelectedSavedBtn: document.getElementById('downloadSelectedSavedBtn'),
    deleteSelectedSavedBtn: document.getElementById('deleteSelectedSavedBtn'),
    savedLinksContainer: document.getElementById('savedLinksContainer')
}

// 背景图管理功能
const backgroundManager = {
    backgrounds: [],
    
    init() {
        this.loadBackgrounds();
        this.bindEvents();
    },
    
    bindEvents() {
        // 刷新按钮
        const refreshBtn = document.getElementById('refreshBackgroundsBtn');
        if (refreshBtn) {
            refreshBtn.addEventListener('click', () => {
                this.loadBackgrounds();
                showToast('背景图列表已刷新', 'success');
            });
        }
        
        // 删除所有背景图按钮
        const deleteAllBtn = document.getElementById('deleteAllBackgroundsBtn');
        if (deleteAllBtn) {
            deleteAllBtn.addEventListener('click', async () => {
                const confirmed = await showCustomConfirm(
                    '确认删除所有背景图', 
                    '确定要删除所有背景图吗？此操作不可撤销！'
                );
                if (confirmed) {
                    this.deleteAllBackgrounds();
                }
            });
        }
    },
    
    // 加载背景图列表
    async loadBackgrounds() {
        try {
            const response = await fetch('/api/backgrounds');
            if (!response.ok) {
                throw new Error('获取背景图列表失败');
            }
            
            const data = await response.json();
            this.backgrounds = data.backgrounds || [];
            this.updateDisplay(data);
        } catch (error) {
            console.error('加载背景图列表失败:', error);
            showToast('加载背景图列表失败: ' + error.message, 'error');
            this.updateDisplay({ backgrounds: [], totalCount: 0, totalSizeFormatted: '0 B' });
        }
    },
    
    // 删除单个背景图
    async deleteBackground(filename) {
        try {
            const confirmed = await showCustomConfirm(
                '确认删除背景图', 
                `确定要删除背景图 "${filename}" 吗？此操作不可撤销！`
            );
            if (!confirmed) return;
            
            const response = await fetch('/api/delete-background', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ filename })
            });
            
            const result = await response.json();
            
            if (result.success) {
                showToast('背景图删除成功', 'success');
                // 从列表中移除并更新显示
                this.backgrounds = this.backgrounds.filter(bg => bg.name !== filename);
                this.updateDisplayFromCache();
            } else {
                showToast('删除失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('删除背景图失败:', error);
            showToast('删除背景图失败: ' + error.message, 'error');
        }
    },
    
    // 删除所有背景图
    async deleteAllBackgrounds() {
        try {
            const response = await fetch('/api/delete-all-backgrounds', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            
            const result = await response.json();
            
            if (result.success) {
                showToast(`成功删除 ${result.deletedCount} 个背景图文件`, 'success');
                this.backgrounds = [];
                this.updateDisplayFromCache();
            } else {
                showToast('删除失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('删除所有背景图失败:', error);
            showToast('删除所有背景图失败: ' + error.message, 'error');
        }
    },
    
    // 更新显示（从缓存）
    updateDisplayFromCache() {
        const totalSize = this.backgrounds.reduce((sum, bg) => sum + bg.size, 0);
        const data = {
            backgrounds: this.backgrounds,
            totalCount: this.backgrounds.length,
            totalSizeFormatted: this.formatFileSize(totalSize)
        };
        this.updateDisplay(data);
    },
    
    // 更新显示
    updateDisplay(data) {
        const backgroundsList = document.getElementById('backgroundsList');
        const backgroundStats = document.getElementById('backgroundStats');
        
        if (!backgroundsList) return;
        
        // 更新统计信息
        if (backgroundStats) {
            backgroundStats.innerHTML = `
                <div class="stat-item">
                    <i class="fas fa-images"></i>
                    <span>背景图数量: ${data.totalCount}</span>
                </div>
                <div class="stat-item">
                    <i class="fas fa-hdd"></i>
                    <span>总大小: ${data.totalSizeFormatted}</span>
                </div>
            `;
        }
        
        // 更新背景图列表
        if (data.backgrounds.length === 0) {
            backgroundsList.innerHTML = '<div class="no-backgrounds">暂无背景图文件</div>';
            return;
        }
        
        const html = data.backgrounds.map(background => `
            <div class="background-item">
                <img src="${background.path}" alt="${background.name}" class="background-thumbnail" 
                     onerror="this.src=''" />
                <div class="background-info">
                    <div class="background-name">${background.name}</div>
                    <div class="background-details">
                        <div class="background-detail">
                            <i class="fas fa-calendar"></i>
                            <span>${new Date(background.modifiedAt).toLocaleString()}</span>
                        </div>
                        <div class="background-detail">
                            <i class="fas fa-hdd"></i>
                            <span>${background.sizeFormatted}</span>
                        </div>
                        <div class="background-detail">
                            <i class="fas fa-file-image"></i>
                            <span>${background.extension.toUpperCase()}</span>
                        </div>
                    </div>
                </div>
                <div class="background-actions">
                    <button class="btn btn-small btn-primary" onclick="backgroundManager.previewBackground('${background.path}', '${background.name}')" title="预览">
                        <i class="fas fa-eye"></i>
                    </button>
                    <button class="btn btn-small btn-danger" onclick="backgroundManager.deleteBackground('${background.name}')" title="删除">
                        <i class="fas fa-trash"></i>
                    </button>
                </div>
            </div>
        `).join('');
        
        backgroundsList.innerHTML = html;
    },
    
    // 预览背景图
    previewBackground(imagePath, imageName) {
        // 创建预览模态框
        const modal = document.createElement('div');
        modal.className = 'background-preview-modal';
        modal.innerHTML = `
            <div class="background-preview-content">
                <div class="background-preview-header">
                    <h3><i class="fas fa-eye"></i> 背景图预览</h3>
                    <button class="background-preview-close">&times;</button>
                </div>
                <div class="background-preview-body">
                    <div class="background-preview-image-container">
                        <img src="${imagePath}" alt="${imageName}" class="background-preview-image" />
                    </div>
                    <div class="background-preview-info">
                        <h4>${imageName}</h4>
                        <p>点击图片可查看原始大小</p>
                    </div>
                </div>
            </div>
        `;
        
        document.body.appendChild(modal);
        
        // 绑定关闭事件
        const closeBtn = modal.querySelector('.background-preview-close');
        const closeModal = () => {
            document.body.removeChild(modal);
        };
        
        closeBtn.addEventListener('click', closeModal);
        modal.addEventListener('click', (e) => {
            if (e.target === modal) closeModal();
        });
        
        // 点击图片在新窗口打开
        const previewImage = modal.querySelector('.background-preview-image');
        previewImage.addEventListener('click', () => {
            window.open(imagePath, '_blank');
        });
        
        // ESC键关闭
        const handleEsc = (e) => {
            if (e.key === 'Escape') {
                closeModal();
                document.removeEventListener('keydown', handleEsc);
            }
        };
        document.addEventListener('keydown', handleEsc);
    },
    
    // 格式化文件大小
    formatFileSize(bytes) {
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
};

// 设置管理器
const settingsManager = {
    init() {
        this.bindEvents();
        this.loadSettings();
    },

    bindEvents() {
        // 设置按钮点击事件
        const settingsBtn = document.getElementById('settingsBtn');
        if (settingsBtn) {
            settingsBtn.addEventListener('click', () => {
                this.showSettingsModal();
            });
        }

        // 重启按钮点击事件
        const restartBtn = document.getElementById('restartBtn');
        if (restartBtn) {
            restartBtn.addEventListener('click', () => {
                this.handleRestart();
            });
        }

        // 设置弹窗关闭事件
        const settingsModal = document.getElementById('settingsModal');
        const closeBtn = settingsModal?.querySelector('.close-btn');
        
        if (closeBtn) {
            closeBtn.addEventListener('click', () => {
                this.hideSettingsModal();
            });
        }

        // 点击弹窗外部关闭
        if (settingsModal) {
            settingsModal.addEventListener('click', (e) => {
                if (e.target === settingsModal) {
                    this.hideSettingsModal();
                }
            });
        }

        // 端口输入框事件
        const portInput = document.getElementById('serverPort');
        if (portInput) {
            portInput.addEventListener('change', () => {
                this.saveSettings();
            });
        }
    },

    showSettingsModal() {
        const modal = document.getElementById('settingsModal');
        if (modal) {
            modal.style.display = 'flex';
            // 添加动画效果
            setTimeout(() => {
                modal.classList.add('show');
            }, 10);
        }
    },

    hideSettingsModal() {
        const modal = document.getElementById('settingsModal');
        if (modal) {
            modal.classList.remove('show');
            setTimeout(() => {
                modal.style.display = 'none';
            }, 300);
        }
    },

    loadSettings() {
        // 从localStorage加载设置
        const savedPort = localStorage.getItem('serverPort');
        const portInput = document.getElementById('serverPort');
        
        if (portInput) {
            portInput.value = savedPort || '3001';
        }
    },

    saveSettings() {
        const portInput = document.getElementById('serverPort');
        
        if (portInput) {
            const port = portInput.value.trim();
            
            // 验证端口号
            if (this.validatePort(port)) {
                localStorage.setItem('serverPort', port);
                showToast('设置已保存，将在下次启动服务时生效', 'success');
            } else {
                showToast('端口号无效，请输入1-65535之间的数字', 'error');
                // 恢复之前的值
                portInput.value = localStorage.getItem('serverPort') || '3001';
            }
        }
    },

    validatePort(port) {
        const portNum = parseInt(port);
        return !isNaN(portNum) && portNum >= 1 && portNum <= 65535;
    },

    async handleRestart() {
        const restartBtn = document.getElementById('restartBtn');
        if (!restartBtn) return;

        // 显示确认对话框
        const confirmed = await this.showConfirmDialog(
            '重启服务',
            '确定要重启服务器吗？这将中断当前的下载任务。'
        );

        if (!confirmed) return;

        try {
            // 更新按钮状态
            restartBtn.classList.add('restarting');
            restartBtn.title = '正在重启...';
            
            // 显示重启提示
            toastManager.info('正在重启服务器，请稍候...', 5000);
            
            // 检查是否在Electron环境中
            if (window.electronAPI && window.electronAPI.isElectron) {
                // 在Electron环境中，使用IPC调用主进程的重启功能
                console.log('使用Electron IPC重启服务器');
                const result = await window.electronAPI.restartServer();
                
                if (result.success) {
                    toastManager.success('服务器重启成功！', 3000);
                    // 等待一段时间后刷新页面
                    setTimeout(() => {
                        window.location.reload();
                    }, 2000);
                } else {
                    throw new Error(result.message);
                }
            } else {
                // 在浏览器环境中，使用原来的socket方式
                console.log('使用Socket重启服务器');
                socket.emit('restart-server');
                
                // 等待一段时间后刷新页面
                setTimeout(() => {
                    window.location.reload();
                }, 3000);
            }
            
        } catch (error) {
            console.error('重启失败:', error);
            toastManager.error(`重启失败: ${error.message || '请手动重启服务器'}`);
            
            // 恢复按钮状态
            restartBtn.classList.remove('restarting');
            restartBtn.title = '重启服务';
        }
    },

    showConfirmDialog(title, message) {
        return new Promise((resolve) => {
            const modal = document.getElementById('customConfirmModal');
            const titleEl = document.getElementById('confirmTitle');
            const messageEl = document.getElementById('confirmMessage');
            const cancelBtn = document.getElementById('confirmCancelBtn');
            const okBtn = document.getElementById('confirmOkBtn');
            
            if (!modal || !titleEl || !messageEl || !cancelBtn || !okBtn) {
                resolve(false);
                return;
            }
            
            titleEl.textContent = title;
            messageEl.textContent = message;
            modal.style.display = 'flex';
            
            const handleCancel = () => {
                modal.style.display = 'none';
                cancelBtn.removeEventListener('click', handleCancel);
                okBtn.removeEventListener('click', handleOk);
                resolve(false);
            };
            
            const handleOk = () => {
                modal.style.display = 'none';
                cancelBtn.removeEventListener('click', handleCancel);
                okBtn.removeEventListener('click', handleOk);
                resolve(true);
            };
            
            cancelBtn.addEventListener('click', handleCancel);
            okBtn.addEventListener('click', handleOk);
        });
    }
}

// 文件清理管理器
const cleanupManager = {
    currentType: 'downloads', // 'downloads' 或 'outputs'
    
    init() {
        this.bindEvents();
    },
    
    bindEvents() {
        // 绑定清理按钮事件
        const cleanupDownloadsBtn = document.getElementById('cleanupDownloadsBtn');
        const cleanupOutputsBtn = document.getElementById('cleanupOutputsBtn');
        
        if (cleanupDownloadsBtn) {
            cleanupDownloadsBtn.addEventListener('click', () => {
                this.currentType = 'downloads';
                this.showCleanupModal();
            });
        }
        
        if (cleanupOutputsBtn) {
            cleanupOutputsBtn.addEventListener('click', () => {
                this.currentType = 'outputs';
                this.showCleanupModal();
            });
        }
        
        // 绑定弹窗事件
        const cleanupModal = document.getElementById('cleanupModal');
        const cleanupCloseBtn = document.getElementById('cleanupCloseBtn');
        const cleanupCancelBtn = document.getElementById('cleanupCancelBtn');
        const cleanupPreviewBtn = document.getElementById('cleanupPreviewBtn');
        const cleanupConfirmBtn = document.getElementById('cleanupConfirmBtn');
        
        if (cleanupCloseBtn) {
            cleanupCloseBtn.addEventListener('click', () => this.hideCleanupModal());
        }
        
        if (cleanupCancelBtn) {
            cleanupCancelBtn.addEventListener('click', () => this.hideCleanupModal());
        }
        
        if (cleanupPreviewBtn) {
            cleanupPreviewBtn.addEventListener('click', () => this.previewCleanup());
        }
        
        if (cleanupConfirmBtn) {
            cleanupConfirmBtn.addEventListener('click', () => this.executeCleanup());
        }
        
        // 点击遮罩层关闭弹窗
        if (cleanupModal) {
            cleanupModal.addEventListener('click', (e) => {
                if (e.target === cleanupModal) {
                    this.hideCleanupModal();
                }
            });
        }
        
        // 日期输入框变化时重置预览
        const cleanupDate = document.getElementById('cleanupDate');
        if (cleanupDate) {
            cleanupDate.addEventListener('change', () => {
                this.resetPreview();
            });
        }
    },
    
    showCleanupModal() {
        const modal = document.getElementById('cleanupModal');
        const title = document.getElementById('cleanupSectionTitle');
        const cleanupDate = document.getElementById('cleanupDate');
        
        if (!modal) return;
        
        // 设置标题
        if (title) {
            if (this.currentType === 'downloads') {
                title.innerHTML = '<i class="fas fa-download"></i> 清理下载文件';
            } else {
                title.innerHTML = '<i class="fas fa-archive"></i> 清理产出物文件';
            }
        }
        
        // 设置默认日期为7天前
        if (cleanupDate) {
            const defaultDate = new Date();
            defaultDate.setDate(defaultDate.getDate() - 7);
            cleanupDate.value = defaultDate.toISOString().split('T')[0];
        }
        
        // 重置预览
        this.resetPreview();
        
        // 显示弹窗
        modal.style.display = 'flex';
        setTimeout(() => {
            modal.classList.add('show');
        }, 10);
    },
    
    hideCleanupModal() {
        const modal = document.getElementById('cleanupModal');
        if (!modal) return;
        
        modal.classList.remove('show');
        setTimeout(() => {
            modal.style.display = 'none';
        }, 300);
    },
    
    resetPreview() {
        const preview = document.getElementById('cleanupPreview');
        const confirmBtn = document.getElementById('cleanupConfirmBtn');
        
        if (preview) {
            preview.style.display = 'none';
        }
        
        if (confirmBtn) {
            confirmBtn.disabled = true;
        }
    },
    
    async previewCleanup() {
        const cleanupDate = document.getElementById('cleanupDate');
        const previewBtn = document.getElementById('cleanupPreviewBtn');
        
        if (!cleanupDate || !cleanupDate.value) {
            showToast('请选择清理日期', 'warning');
            return;
        }
        
        // 禁用预览按钮
        if (previewBtn) {
            previewBtn.disabled = true;
            previewBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 预览中...';
        }
        
        try {
            const response = await fetch('/api/cleanup-preview', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    type: this.currentType,
                    date: cleanupDate.value
                })
            });
            
            const result = await response.json();
            
            if (result.success) {
                this.showPreview(result);
            } else {
                showToast('预览失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('预览失败:', error);
            showToast('预览失败: ' + error.message, 'error');
        } finally {
            // 恢复预览按钮
            if (previewBtn) {
                previewBtn.disabled = false;
                previewBtn.innerHTML = '<i class="fas fa-eye"></i> 预览';
            }
        }
    },
    
    showPreview(data) {
        const preview = document.getElementById('cleanupPreview');
        const fileCount = document.getElementById('previewFileCount');
        const totalSize = document.getElementById('previewTotalSize');
        const previewList = document.getElementById('previewList');
        const confirmBtn = document.getElementById('cleanupConfirmBtn');
        
        if (!preview) return;
        
        // 更新统计信息
        if (fileCount) {
            fileCount.textContent = data.fileCount;
        }
        
        if (totalSize) {
            totalSize.textContent = this.formatFileSize(data.totalSize);
        }
        
        // 更新文件列表
        if (previewList) {
            if (data.files.length === 0) {
                previewList.innerHTML = '<div class="preview-file-item"><i class="fas fa-info-circle"></i> 没有找到符合条件的文件</div>';
            } else {
                previewList.innerHTML = data.files.map(file => `
                    <div class="preview-file-item">
                        <i class="fas fa-file"></i>
                        <span>${file.path}</span>
                        <span style="margin-left: auto; color: #666; font-size: 0.8rem;">
                            ${this.formatFileSize(file.size)} | ${new Date(file.mtime).toLocaleDateString()}
                        </span>
                    </div>
                `).join('');
            }
        }
        
        // 显示预览区域
        preview.style.display = 'block';
        
        // 启用确认按钮
        if (confirmBtn && data.fileCount > 0) {
            confirmBtn.disabled = false;
        }
    },
    
    async executeCleanup() {
        const cleanupDate = document.getElementById('cleanupDate');
        const confirmBtn = document.getElementById('cleanupConfirmBtn');
        
        if (!cleanupDate || !cleanupDate.value) {
            showToast('请选择清理日期', 'warning');
            return;
        }
        
        // 二次确认
        const confirmed = await showCustomConfirm(
            '确认文件清理',
            `确定要删除 ${cleanupDate.value} 之前的所有${this.currentType === 'downloads' ? '下载' : '产出物'}文件吗？此操作不可撤销。`
        );
        
        if (!confirmed) {
            return;
        }
        
        // 禁用确认按钮
        if (confirmBtn) {
            confirmBtn.disabled = true;
            confirmBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 清理中...';
        }
        
        try {
            const response = await fetch('/api/cleanup-execute', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    type: this.currentType,
                    date: cleanupDate.value
                })
            });
            
            const result = await response.json();
            
            if (result.success) {
                showToast(result.message, 'success');
                this.hideCleanupModal();
                
                // 刷新文件列表
                if (this.currentType === 'downloads') {
                    loadDownloadsList();
                } else {
                    loadOutputsList();
                }
            } else {
                showToast('清理失败: ' + result.error, 'error');
            }
        } catch (error) {
            console.error('清理失败:', error);
            showToast('清理失败: ' + error.message, 'error');
        } finally {
            // 恢复确认按钮
            if (confirmBtn) {
                confirmBtn.disabled = false;
                confirmBtn.innerHTML = '<i class="fas fa-trash"></i> 确认删除';
            }
        }
    },
    
    formatFileSize(bytes) {
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
};;

// 切换树形视图模式
async function switchTreeMode(button, mode) {
    // 更新按钮状态
    const allButtons = button.parentElement.querySelectorAll('.tree-mode-btn');
    allButtons.forEach(btn => btn.classList.remove('active'));
    button.classList.add('active');
    
    // 更新描述
    const descElement = document.getElementById('treeModeDesc');
    const treeContent = document.getElementById('treeContent');
    
    if (mode === 'smart') {
        descElement.textContent = '按文件名智能分组显示';
        
        // 显示智能分组
        const availableFiles = videoProcessing.getAvailableVideoFiles();
        const fileTree = videoProcessing.buildFileTree(availableFiles);
        
        // 保留"使用前一步输出"选项
        const useOutputOption = treeContent.querySelector('.file-option.recommended');
        let html = '';
        if (useOutputOption) {
            html += useOutputOption.outerHTML;
        }
        html += videoProcessing.renderFileTree(fileTree);
        
        treeContent.innerHTML = html;
        
    } else if (mode === 'directory') {
        descElement.textContent = '按实际目录结构显示';
        
        try {
            // 显示加载状态
            treeContent.innerHTML = `
                <div class="loading-state">
                    <i class="fas fa-spinner fa-spin"></i>
                    <span>正在加载目录结构...</span>
                </div>`;
            
            // 获取实际目录结构
            const response = await fetch('/api/downloads/directory-structure');
            const directoryData = await response.json();
            
            // 保留"使用前一步输出"选项
            const useOutputOption = treeContent.querySelector('.file-option.recommended');
            let html = '';
            if (useOutputOption) {
                html += useOutputOption.outerHTML;
            }
            
            if (directoryData && directoryData.children && directoryData.children.length > 0) {
                // 渲染目录结构
                directoryData.children.forEach(child => {
                    html += videoProcessing.renderDirectoryStructure(child, 0);
                });
            } else {
                html += `
                    <div class="empty-state">
                        <i class="fas fa-folder-open"></i>
                        <p>暂无下载文件</p>
                    </div>`;
            }
            
            treeContent.innerHTML = html;
            
        } catch (error) {
            console.error('获取目录结构失败:', error);
            treeContent.innerHTML = `
                <div class="error-state">
                    <i class="fas fa-exclamation-triangle"></i>
                    <p>加载目录结构失败</p>
                </div>`;
        }
    }
    
    // 重新初始化文件选项事件
    const fileSelector = treeContent.closest('.file-selector');
    const selectorId = fileSelector.id;
    
    // 检查是否是合并文件选择器模态框
    const modal = fileSelector.closest('#mergeFileSelectModal');
    if (modal) {
        // 如果是合并文件选择器，使用合并模式的事件初始化
        mergeFileManager.initMergeFileSelector(selectorId);
    } else {
        // 否则使用普通的文件选项事件初始化
        videoProcessing.initFileOptionEvents(selectorId);
    }
};

// 应用状态
const appState = {
    ytdlpInstalled: false,
    downloadMode: 'single', // 'single', 'batch', 'search', or 'saved'
    batchUrls: [],
    batchVideoInfos: [],
    batchKeyword: '', // 批量下载关键字
    searchResults: [],
    selectedVideos: [],
    
    // 新增状态
    connectionChecked: false,
    youtubeAccessible: false,
    
    // 文件加载状态
    filesLoading: true,
    filesLoaded: false,
    filesLoadError: false,
    
    searchPagination: {
        currentPage: 1,
        totalPages: 1,
        itemsPerPage: 10,
        totalItems: 0
    },
    // 搜索上下文
    currentSearchContext: {
        searchType: null,
        hashtag: null,
        keyword: null,
        videoType: null
    },
    
    // 记录链接状态
    savedLinks: [],
    selectedSavedLinks: [],
    currentViewMode: 'list', // 'list' or 'tags'
    currentTagFilter: null,
    availableTags: []
};

// 应用模式管理
const appMode = {
    current: 'download',
    
    init() {
        this.setupModeListeners();
        this.setupFileModeListeners();
        this.updateModeDisplay();
    },
    
    setupModeListeners() {
        document.querySelectorAll('input[name="appMode"]').forEach(radio => {
            radio.addEventListener('change', (e) => {
                this.switchMode(e.target.value);
            });
        });
    },
    
    setupFileModeListeners() {
        document.querySelectorAll('input[name="fileMode"]').forEach(radio => {
            radio.addEventListener('change', (e) => {
                this.switchFileMode(e.target.value);
            });
        });
    },
    
    switchMode(mode) {
        const previousMode = this.current;
        
        // 使用全局状态管理器处理模式切换
        if (typeof globalStateManager !== 'undefined') {
            // 将模式名称映射到状态管理器的模块名称
            const moduleMap = {
                'download': 'download',
                'processing': 'videoProcessing',
                'filemanagement': 'fileManagement'
            };
            
            const fromModule = moduleMap[previousMode];
            const toModule = moduleMap[mode];
            
            if (fromModule && toModule) {
                globalStateManager.switchModule(fromModule, toModule);
            }
        }
        
        this.current = mode;
        this.updateModeDisplay();
        
        // 显示相应的消息
        if (mode === 'processing') {
            showMessage('已切换到视频处理模式', 'info');
        } else if (mode === 'filemanagement') {
            showMessage('已切换到文件管理模式', 'info');
        } else {
            showMessage('已切换到下载模式', 'info');
        }
    },
    
    switchFileMode(mode) {
        // 保存当前文件管理状态
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.saveFileManagementState();
        }
        
        // 同步更新单选按钮的选中状态
        const fileModeRadios = document.querySelectorAll('input[name="fileMode"]');
        fileModeRadios.forEach(radio => {
            radio.checked = (radio.value === mode);
        });
        
        const downloadsManagement = document.getElementById('downloadsManagement');
        const outputsManagement = document.getElementById('outputsManagement');
        const backgroundsManagement = document.getElementById('backgroundsManagement');
        
        if (mode === 'outputs') {
            if (downloadsManagement) downloadsManagement.style.display = 'none';
            if (outputsManagement) outputsManagement.style.display = 'block';
            if (backgroundsManagement) backgroundsManagement.style.display = 'none';
            
            // 刷新产出物列表
            if (typeof outputManager !== 'undefined') {
                outputManager.updateDisplay();
            }
        } else if (mode === 'backgrounds') {
            if (downloadsManagement) downloadsManagement.style.display = 'none';
            if (outputsManagement) outputsManagement.style.display = 'none';
            if (backgroundsManagement) backgroundsManagement.style.display = 'block';
            
            // 刷新背景图列表
            if (typeof backgroundManager !== 'undefined') {
                backgroundManager.loadBackgrounds();
            }
        } else {
            if (downloadsManagement) downloadsManagement.style.display = 'block';
            if (outputsManagement) outputsManagement.style.display = 'none';
            if (backgroundsManagement) backgroundsManagement.style.display = 'none';
            
            // 检查文件加载状态
            if (appState.filesLoading) {
                // 显示加载状态
                showFileLoadingState();
            } else if (appState.filesLoadError) {
                // 显示错误状态并重试
                showFileLoadErrorState();
            } else {
                // 文件已加载，刷新显示
                loadDownloadsList();
            }
        }
        
        // 更新状态管理器中的文件模式
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.updateModuleState('fileManagement', {
                currentFileMode: mode
            });
        }
    },
    
    updateModeDisplay() {
        const downloadSection = document.querySelector('.download-section');
        const videoProcessingSection = document.getElementById('videoProcessingSection');
        const fileManagementSection = document.getElementById('fileManagementSection');
        const downloadsSection = document.querySelector('.downloads-section');
        
        if (this.current === 'processing') {
            // 显示视频处理界面
            if (downloadSection) downloadSection.style.display = 'none';
            if (videoProcessingSection) videoProcessingSection.style.display = 'block';
            if (fileManagementSection) fileManagementSection.style.display = 'none';
            if (downloadsSection) downloadsSection.style.display = 'none';
            
            // 隐藏其他区域
            document.getElementById('infoSection').style.display = 'none';
            document.getElementById('batchInfoSection').style.display = 'none';
            document.getElementById('progressSection').style.display = 'none';
            document.getElementById('batchProgressSection').style.display = 'none';
            
            // 异步加载视频文件，不阻塞界面
            if (typeof videoProcessing !== 'undefined') {
                if (appState.filesLoaded) {
                    videoProcessing.loadDownloadedFiles();
                } else if (appState.filesLoading) {
                    // 显示加载状态
                    showVideoProcessingLoadingState();
                } else if (appState.filesLoadError) {
                    // 显示错误状态
                    showVideoProcessingErrorState();
                }
            }
        } else if (this.current === 'filemanagement') {
            // 显示文件管理界面
            if (downloadSection) downloadSection.style.display = 'none';
            if (videoProcessingSection) videoProcessingSection.style.display = 'none';
            if (fileManagementSection) fileManagementSection.style.display = 'block';
            if (downloadsSection) downloadsSection.style.display = 'none';
            
            // 隐藏其他区域
            document.getElementById('infoSection').style.display = 'none';
            document.getElementById('batchInfoSection').style.display = 'none';
            document.getElementById('progressSection').style.display = 'none';
            document.getElementById('batchProgressSection').style.display = 'none';
            document.getElementById('processingProgress').style.display = 'none';
            document.getElementById('processingResults').style.display = 'none';
            
            // 初始化文件管理模式
            this.switchFileMode('downloads');
        } else {
            // 显示下载界面
            if (downloadSection) downloadSection.style.display = 'block';
            if (videoProcessingSection) videoProcessingSection.style.display = 'none';
            if (fileManagementSection) fileManagementSection.style.display = 'none';
            if (downloadsSection) downloadsSection.style.display = 'block';
            
            // 隐藏视频处理相关区域
            document.getElementById('processingProgress').style.display = 'none';
            document.getElementById('processingResults').style.display = 'none';
        }
    }
};

// 应用初始化状态管理
const initializationState = {
    ytDlpChecked: false,
    youtubeConnected: false,
    filesLoaded: false,
    eventListenersSetup: false,
    socketListenersSetup: false,
    batchDownloadInitialized: false,
    appModeInitialized: false,
    
    // 检查是否所有功能都已初始化
    isFullyInitialized() {
        return this.ytDlpChecked && this.youtubeConnected && this.filesLoaded && 
               this.eventListenersSetup && this.socketListenersSetup && 
               this.batchDownloadInitialized && this.appModeInitialized;
    },
    
    // 更新初始化状态并检查是否完成
    updateState(key, value) {
        this[key] = value;
        this.checkInitializationComplete();
    },
    
    // 检查初始化是否完成
    checkInitializationComplete() {
        if (this.isFullyInitialized()) {
            this.showInitializationComplete();
        }
    },
    
    // 显示初始化完成提示
    showInitializationComplete() {
        // 隐藏初始化提示
        const initIndicator = document.getElementById('initializationIndicator');
        if (initIndicator) {
            initIndicator.style.display = 'none';
        }
        
        // 显示完成提示
        showToast('🎉 应用初始化完成，所有功能已可用！', 'success', 3000);
        
        // 启用所有功能按钮
        this.enableAllFeatures();
    },
    
    // 启用所有功能
    enableAllFeatures() {
        // 启用模式切换按钮
        document.querySelectorAll('input[name="appMode"]').forEach(radio => {
            radio.disabled = false;
        });
        
        // 启用其他主要功能按钮
        const mainButtons = document.querySelectorAll('.btn:not(.always-enabled)');
        mainButtons.forEach(btn => {
            if (btn.hasAttribute('data-init-disabled')) {
                btn.disabled = false;
                btn.removeAttribute('data-init-disabled');
            }
        });
    }
};

// 初始化应用
async function init() {
    // 显示初始化指示器
    showInitializationIndicator();
    
    // 禁用主要功能直到初始化完成
    disableMainFeatures();
    
    try {
        // 检查yt-dlp状态
        await checkYtDlpStatus();
        initializationState.updateState('ytDlpChecked', true);
        
        // 检查YouTube连接
        await checkYouTubeConnection();
        initializationState.updateState('youtubeConnected', true);
        
        // 异步加载文件列表
        loadDownloadsListAsync().then(() => {
            initializationState.updateState('filesLoaded', true);
        });
        
        // 设置事件监听器
        setupEventListeners();
        initializationState.updateState('eventListenersSetup', true);
        
        // 设置Socket监听器
        setupSocketListeners();
        initializationState.updateState('socketListenersSetup', true);
        
        // 初始化批量下载
        initializeBatchDownload();
        initializationState.updateState('batchDownloadInitialized', true);
        
        // 初始化应用模式管理
        appMode.init();
        initializationState.updateState('appModeInitialized', true);
        
        // 设置每分钟检查一次YouTube连接
        setInterval(() => {
            checkYouTubeConnection();
        }, 60000); // 60秒 = 1分钟
        
    } catch (error) {
        console.error('应用初始化失败:', error);
        showToast('⚠️ 应用初始化过程中出现问题，部分功能可能不可用', 'warning', 5000);
        
        // 即使出错也要启用基本功能
        setTimeout(() => {
            initializationState.enableAllFeatures();
            const initIndicator = document.getElementById('initializationIndicator');
            if (initIndicator) {
                initIndicator.style.display = 'none';
            }
        }, 2000);
    }
}

// 显示初始化指示器
function showInitializationIndicator() {
    const indicator = document.createElement('div');
    indicator.id = 'initializationIndicator';
    indicator.className = 'initialization-indicator';
    indicator.innerHTML = `
        <div class="init-content">
            <div class="init-spinner"></div>
            <div class="init-text">
                <h3>🚀 应用初始化中...</h3>
                <p>正在检查系统状态和加载必要组件，请稍候</p>
            </div>
        </div>
    `;
    
    document.body.appendChild(indicator);
}

// 禁用主要功能
function disableMainFeatures() {
    // 禁用模式切换按钮
    document.querySelectorAll('input[name="appMode"]').forEach(radio => {
        radio.disabled = true;
    });
    
    // 禁用主要功能按钮（保留一些基本按钮可用）
    const mainButtons = document.querySelectorAll('.btn:not(.always-enabled)');
    mainButtons.forEach(btn => {
        if (!btn.disabled) {
            btn.setAttribute('data-init-disabled', 'true');
            btn.disabled = true;
        }
    });
}

// 显示文件加载状态
function showFileLoadingState() {
    const fileList = document.getElementById('fileList');
    if (fileList) {
        fileList.innerHTML = `
            <div class="loading-state">
                <div class="loading-spinner"></div>
                <p>文件信息加载中...</p>
            </div>
        `;
    }
}

// 显示文件加载错误状态
function showFileLoadErrorState() {
    const fileList = document.getElementById('fileList');
    if (fileList) {
        fileList.innerHTML = `
            <div class="error-state">
                <p>文件信息加载失败</p>
                <button onclick="retryLoadFiles()" class="btn-retry">重试</button>
            </div>
        `;
    }
}

// 显示视频处理模式的文件加载状态
function showVideoProcessingLoadingState() {
    const availableFiles = document.getElementById('availableFiles');
    if (availableFiles) {
        availableFiles.innerHTML = `
            <div class="loading-state">
                <div class="loading-spinner"></div>
                <p>视频文件加载中...</p>
            </div>
        `;
    }
}

// 显示视频处理模式的文件加载错误状态
function showVideoProcessingErrorState() {
    const availableFiles = document.getElementById('availableFiles');
    if (availableFiles) {
        availableFiles.innerHTML = `
            <div class="error-state">
                <p>视频文件加载失败</p>
                <button onclick="retryLoadVideoFiles()" class="btn-retry">重试</button>
            </div>
        `;
    }
}

// 重试加载视频文件
function retryLoadVideoFiles() {
    appState.filesLoading = true;
    appState.filesLoadError = false;
    showVideoProcessingLoadingState();
    loadDownloadsListAsync();
}

// 重试加载文件
function retryLoadFiles() {
    appState.filesLoading = true;
    appState.filesLoadError = false;
    showFileLoadingState();
    loadDownloadsListAsync();
}

// 异步加载下载列表，不阻塞其他功能
async function loadDownloadsListAsync() {
    try {
        appState.filesLoading = true;
        appState.filesLoadError = false;
        
        // 如果当前在文件管理模式，显示加载状态
        const currentMode = document.querySelector('.mode-btn.active')?.dataset.mode;
        if (currentMode === 'file') {
            showFileLoadingState();
        } else if (currentMode === 'processing') {
            showVideoProcessingLoadingState();
        }
        
        await loadDownloadsList();
        
        appState.filesLoading = false;
        appState.filesLoaded = true;
        
        // 根据当前模式刷新显示
        if (currentMode === 'file') {
            loadDownloadsList();
        } else if (currentMode === 'processing' && typeof videoProcessing !== 'undefined') {
            videoProcessing.loadDownloadedFiles();
        }
    } catch (error) {
        console.error('加载文件列表失败:', error);
        appState.filesLoading = false;
        appState.filesLoadError = true;
        
        // 根据当前模式显示错误状态
        const currentMode = document.querySelector('.mode-btn.active')?.dataset.mode;
        if (currentMode === 'file') {
            showFileLoadErrorState();
        } else if (currentMode === 'processing') {
            showVideoProcessingErrorState();
        }
    }
}

// 初始化批量下载状态
function initializeBatchDownload() {
    // 设置初始按钮状态
    elements.validateUrlsBtn.disabled = true;
    elements.clearUrlsBtn.disabled = true;
    elements.batchGetInfoBtn.disabled = true;
    elements.batchDownloadBtn.disabled = true;
    
    // 设置搜索下载初始状态
    elements.downloadSelectedBtn.disabled = true;
    
    // 确保初始显示状态正确
    if (appState.downloadMode === 'single') {
        elements.singleDownloadSection.style.display = 'block';
        elements.batchDownloadSection.style.display = 'none';
        elements.searchDownloadSection.style.display = 'none';
        document.getElementById('singleButtons').style.display = 'flex';
        document.getElementById('batchButtons').style.display = 'none';
        elements.searchButtons.style.display = 'none';
    }
    
    // 隐藏所有结果区域
    elements.searchResults.style.display = 'none';
}

// 检查 YouTube 连接状态
async function checkYouTubeConnection() {
    if (!elements.connectionStatus || !elements.connectionText) {
        return; // 如果元素不存在，跳过检查
    }
    
    // 显示检查中状态
    elements.connectionStatus.className = 'connection-status checking';
    elements.connectionText.innerHTML = '正在检查YouTube连接<span class="dots">...</span>';
    
    try {
        // 设置请求超时
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
        
        const response = await fetch('/api/check-youtube', {
            method: 'GET',
            signal: controller.signal,
            headers: {
                'Content-Type': 'application/json'
            }
        });
        
        clearTimeout(timeoutId);
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        const data = await response.json();
        
        appState.connectionChecked = true;
        appState.youtubeAccessible = data.accessible;
        
        if (data.accessible) {
            elements.connectionStatus.className = 'connection-status connected';
            elements.connectionText.textContent = 'YouTube 连接正常';
            console.log('YouTube 连接检查成功:', data);
        } else {
            elements.connectionStatus.className = 'connection-status disconnected';
            elements.connectionText.textContent = data.message || 'YouTube 连接失败 - 可能需要代理';
            console.warn('YouTube 连接失败:', data);
        }
    } catch (error) {
        console.error('检查 YouTube 连接失败:', error);
        appState.connectionChecked = true;
        appState.youtubeAccessible = false;
        
        if (error.name === 'AbortError') {
            elements.connectionStatus.className = 'connection-status error';
            elements.connectionText.textContent = 'YouTube 连接检查超时 - 请检查网络';
        } else if (error.message.includes('Failed to fetch')) {
            elements.connectionStatus.className = 'connection-status error';
            elements.connectionText.textContent = '无法连接到服务器 - 请检查服务器状态';
        } else {
            elements.connectionStatus.className = 'connection-status disconnected';
            elements.connectionText.textContent = 'YouTube 连接检查失败 - ' + error.message;
        }
    }
}

// 检查 yt-dlp 状态
async function checkYtDlpStatus() {
    try {
        const response = await fetch('/api/check');
        const data = await response.json();
        
        if (data.available) {
            elements.ytdlpStatus.innerHTML = '<i class="fas fa-check-circle" style="color: #28a745;"></i> yt-dlp 已安装';
        } else {
            elements.ytdlpStatus.innerHTML = '<i class="fas fa-exclamation-triangle" style="color: #dc3545;"></i> yt-dlp 未安装';
            showMessage('请先安装 yt-dlp: https://github.com/yt-dlp/yt-dlp', 'error');
        }
    } catch (error) {
        elements.ytdlpStatus.innerHTML = '<i class="fas fa-times-circle" style="color: #dc3545;"></i> 检查失败';
        showMessage('无法连接到服务器', 'error');
    }
}

// 设置事件监听器
function setupEventListeners() {
    // 下载模式切换
    elements.downloadModeRadios.forEach(radio => {
        radio.addEventListener('change', handleDownloadModeChange);
    });
    
    // 批量 URL 输入
    elements.batchUrlsTextarea.addEventListener('input', handleBatchUrlsInput);
    
    // 批量操作按钮
    elements.validateUrlsBtn.addEventListener('click', validateBatchUrls);
    elements.clearUrlsBtn.addEventListener('click', clearBatchUrls);
    elements.batchGetInfoBtn.addEventListener('click', getBatchVideoInfo);
    elements.batchDownloadBtn.addEventListener('click', startBatchDownload);
    elements.stopBatchBtn.addEventListener('click', stopBatchDownload);
    
    // 搜索下载操作按钮
    elements.searchBtn.addEventListener('click', performSearch);
    elements.downloadSelectedBtn.addEventListener('click', downloadSelectedVideos);
    elements.clearSearchBtn.addEventListener('click', clearSearchResults);
    elements.selectAllCheckbox.addEventListener('change', handleSelectAll);
    
    // 搜索关键字输入框回车键
    elements.searchKeyword.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            performSearch();
        }
    });
    
    // 并发下载设置
    elements.enableConcurrentCheckbox.addEventListener('change', handleConcurrentChange);
    elements.maxConcurrentSelect.addEventListener('change', handleMaxConcurrentChange);
    
    // 字幕选项切换
    elements.subtitlesCheckbox.addEventListener('change', () => {
        elements.subtitleOptions.style.display = elements.subtitlesCheckbox.checked ? 'block' : 'none';
    });

    // 获取视频信息
    elements.getInfoBtn.addEventListener('click', getVideoInfo);

    // 开始下载
    elements.downloadBtn.addEventListener('click', startDownload);

    // 刷新下载列表
    elements.refreshBtn.addEventListener('click', loadDownloadsList);

    // 清除日志按钮
    const clearLogBtn = document.getElementById('clearLogBtn');
    if (clearLogBtn) {
        clearLogBtn.addEventListener('click', () => {
            const logContainer = document.getElementById('processingLogContainer');
            if (logContainer) {
                logContainer.innerHTML = '';
                showToast('处理日志已清除', 'success', 2000);
            }
        });
    }

    // URL 输入框回车键
    elements.videoUrl.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            getVideoInfo();
        }
    });
    
    // 翻页按钮事件监听
    if (elements.prevPageBtn) {
        elements.prevPageBtn.addEventListener('click', goToPreviousPage);
    }
    if (elements.nextPageBtn) {
        elements.nextPageBtn.addEventListener('click', goToNextPage);
    }
    
    // 文件展示模式切换
    const displayModeRadios = document.querySelectorAll('input[name="displayMode"]');
    displayModeRadios.forEach(radio => {
        radio.addEventListener('change', () => {
            loadDownloadsList();
        });
    });
}

// 批量下载处理函数
function handleDownloadModeChange(event) {
    // 检查是否有正在进行的下载
    if (isDownloading) {
        showToast('单个下载正在进行中，请等待完成后再切换模式', 'warning');
        // 恢复之前的选择
        const currentMode = appState.downloadMode;
        document.querySelector(`input[name="downloadMode"][value="${currentMode}"]`).checked = true;
        return;
    }
    
    if (isBatchDownloading) {
        showToast('批量下载正在进行中，请等待完成后再切换模式', 'warning');
        // 恢复之前的选择
        const currentMode = appState.downloadMode;
        document.querySelector(`input[name="downloadMode"][value="${currentMode}"]`).checked = true;
        return;
    }
    
    appState.downloadMode = event.target.value;
    
    // 隐藏所有下载区域
    elements.singleDownloadSection.style.display = 'none';
    elements.batchDownloadSection.style.display = 'none';
    elements.searchDownloadSection.style.display = 'none';
    elements.savedLinksSection.style.display = 'none';
    
    // 隐藏所有按钮组
    document.getElementById('singleButtons').style.display = 'none';
    document.getElementById('batchButtons').style.display = 'none';
    elements.searchButtons.style.display = 'none';
    
    // 隐藏所有信息和进度区域
    elements.infoSection.style.display = 'none';
    elements.progressSection.style.display = 'none';
    elements.batchInfoSection.style.display = 'none';
    elements.batchProgressSection.style.display = 'none';
    elements.searchResults.style.display = 'none';
    
    if (appState.downloadMode === 'single') {
        // 显示单个下载区域
        elements.singleDownloadSection.style.display = 'block';
        document.getElementById('singleButtons').style.display = 'flex';
    } else if (appState.downloadMode === 'batch') {
        // 显示批量下载区域
        elements.batchDownloadSection.style.display = 'block';
        document.getElementById('batchButtons').style.display = 'flex';
    } else if (appState.downloadMode === 'search') {
        // 显示搜索下载区域
        elements.searchDownloadSection.style.display = 'block';
        elements.searchButtons.style.display = 'flex';
    } else if (appState.downloadMode === 'saved') {
        // 显示记录链接区域
        elements.savedLinksSection.style.display = 'block';
        // 加载保存的链接
        loadSavedLinks();
    }
}

function handleBatchUrlsInput() {
    const urls = getBatchUrlsFromTextarea();
    appState.batchUrls = urls;
    elements.urlCountSpan.textContent = `${urls.length} 个 URL`;
    
    // 启用/禁用按钮
    const hasUrls = urls.length > 0;
    elements.validateUrlsBtn.disabled = !hasUrls;
    elements.clearUrlsBtn.disabled = !hasUrls;
    elements.batchGetInfoBtn.disabled = !hasUrls;
    elements.batchDownloadBtn.disabled = !hasUrls; // 直接启用下载按钮
}

function getBatchUrlsFromTextarea() {
    const text = elements.batchUrlsTextarea.value.trim();
    if (!text) return [];
    
    return text.split('\n')
        .map(line => line.trim())
        .filter(line => line && line.startsWith('http'));
}

function validateBatchUrls() {
    const urls = getBatchUrlsFromTextarea();
    if (urls.length === 0) {
        showToast('请输入有效的 URL', 'warning');
        return;
    }
    
    // 简单的 URL 验证
    const validUrls = urls.filter(url => {
        try {
            new URL(url);
            return url.includes('youtube.com') || url.includes('youtu.be') || url.includes('bilibili.com');
        } catch {
            return false;
        }
    });
    
    if (validUrls.length !== urls.length) {
        const invalidCount = urls.length - validUrls.length;
        showToast(`发现 ${invalidCount} 个无效 URL，已自动过滤`, 'warning');
        
        // 更新文本框内容
        elements.batchUrlsTextarea.value = validUrls.join('\n');
        handleBatchUrlsInput();
    } else {
        showToast(`所有 ${urls.length} 个 URL 都有效`, 'success');
    }
}

function clearBatchUrls() {
    elements.batchUrlsTextarea.value = '';
    elements.batchKeywordInput.value = ''; // 清空关键字输入框
    appState.batchUrls = [];
    appState.batchVideoInfos = [];
    appState.batchKeyword = ''; // 清空应用状态中的关键字
    elements.urlCountSpan.textContent = '0 个 URL';
    elements.validateUrlsBtn.disabled = true;
    elements.clearUrlsBtn.disabled = true;
    elements.batchGetInfoBtn.disabled = true;
    elements.batchDownloadBtn.disabled = true;
    
    // 隐藏信息和进度区域
    elements.batchInfoSection.style.display = 'none';
    elements.batchProgressSection.style.display = 'none';
}

function handleConcurrentChange() {
    const enabled = elements.enableConcurrentCheckbox.checked;
    elements.maxConcurrentSelect.disabled = !enabled;
    maxConcurrentDownloads = enabled ? parseInt(elements.maxConcurrentSelect.value) : 1;
}

function handleMaxConcurrentChange() {
    maxConcurrentDownloads = parseInt(elements.maxConcurrentSelect.value);
}

async function getBatchVideoInfo() {
    const urls = getBatchUrlsFromTextarea();
    if (urls.length === 0) {
        showToast('请输入 URL', 'warning');
        return;
    }
    
    elements.batchGetInfoBtn.disabled = true;
    elements.batchGetInfoBtn.textContent = '获取信息中...';
    
    appState.batchVideoInfos = [];
    elements.batchInfoSection.style.display = 'block';
    elements.batchVideoList.innerHTML = '';
    
    let validCount = 0;
    let invalidCount = 0;
    
    for (let i = 0; i < urls.length; i++) {
        const url = urls[i];
        const itemElement = createBatchVideoItem(url, i);
        elements.batchVideoList.appendChild(itemElement);
        
        try {
            const response = await fetch('/api/info', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ url })
            });
            
            const data = await response.json();
            
            if (response.ok && data.title) {
                validCount++;
                updateBatchVideoItem(itemElement, 'valid', data);
                appState.batchVideoInfos.push({ url, info: data, valid: true });
            } else {
                invalidCount++;
                updateBatchVideoItem(itemElement, 'invalid', { error: data.error || '获取信息失败' });
                appState.batchVideoInfos.push({ url, info: null, valid: false, error: data.error });
            }
        } catch (error) {
            invalidCount++;
            updateBatchVideoItem(itemElement, 'invalid', { error: error.message });
            appState.batchVideoInfos.push({ url, info: null, valid: false, error: error.message });
        }
        
        updateBatchSummary(urls.length, validCount, invalidCount);
    }
    
    elements.batchGetInfoBtn.disabled = false;
    elements.batchGetInfoBtn.textContent = '批量获取信息';
    elements.batchDownloadBtn.disabled = validCount === 0;
}

function createBatchVideoItem(url, index) {
    const item = document.createElement('div');
    item.className = 'batch-video-item loading';
    item.innerHTML = `
        <div class="video-status">⏳</div>
        <div class="video-details">
            <h5>正在获取信息...</h5>
            <p>${url}</p>
        </div>
    `;
    return item;
}

function updateBatchVideoItem(element, status, data) {
    element.className = `batch-video-item ${status}`;
    
    const statusIcon = status === 'valid' ? '✅' : '❌';
    const title = status === 'valid' ? data.title : `错误: ${data.error}`;
    const duration = status === 'valid' && data.duration ? ` (${formatDuration(data.duration)})` : '';
    
    // 添加新字段的显示
    let additionalInfo = '';
    if (status === 'valid' && data) {
        const infoItems = [];
        if (data.orientation) infoItems.push(`方向: ${data.orientation}`);
        if (data.width && data.height) infoItems.push(`分辨率: ${data.width}x${data.height}`);
        if (data.bitrate) infoItems.push(`比特率: ${data.bitrate} kbps`);
        
        if (infoItems.length > 0) {
            additionalInfo = `<div class="video-additional-info">${infoItems.join(' | ')}</div>`;
        }
    }
    
    element.innerHTML = `
        <div class="video-status">${statusIcon}</div>
        <div class="video-details">
            <h5>${title}${duration}</h5>
            <p>${element.querySelector('p').textContent}</p>
            ${additionalInfo}
        </div>
    `;
}

function updateBatchSummary(total, valid, invalid) {
    elements.batchSummary.innerHTML = `
        <div class="summary-item">
            <div class="summary-label">总数</div>
            <span>${total}</span>
        </div>
        <div class="summary-item">
            <div class="summary-label">有效</div>
            <span>${valid}</span>
        </div>
        <div class="summary-item">
            <div class="summary-label">无效</div>
            <span>${invalid}</span>
        </div>
    `;
}

async function startBatchDownload() {
    console.log('开始批量下载...');
    const urls = getBatchUrlsFromTextarea();
    console.log('获取到的URLs:', urls);
    
    if (urls.length === 0) {
        showToast('请输入 URL', 'warning');
        return;
    }
    
    // 获取关键字
    const keyword = elements.batchKeywordInput.value.trim();
    console.log('批量下载关键字:', keyword);
    
    // 如果没有预先获取信息，则创建简单的视频项目
    if (appState.batchVideoInfos.length === 0) {
        console.log('创建简单的视频项目...');
        appState.batchVideoInfos = urls.map(url => ({
            url,
            info: { title: '准备下载...' },
            valid: true
        }));
    }
    
    const validVideos = appState.batchVideoInfos.filter(item => item.valid);
    console.log('有效视频数量:', validVideos.length);
    
    if (validVideos.length === 0) {
        showToast('没有有效的视频可下载', 'error');
        return;
    }
    
    console.log('设置批量下载状态...');
    isBatchDownloading = true;
    batchQueue = [...validVideos];
    currentBatchDownloads = [];
    batchResults = [];
    
    // 保存关键字到应用状态，供下载时使用
    appState.batchKeyword = keyword;
    
    elements.batchDownloadBtn.disabled = true;
    elements.stopBatchBtn.disabled = false;
    elements.batchProgressSection.style.display = 'block';
    
    updateOverallProgress();
    updateDownloadQueue();
    
    console.log('开始处理下载队列...');
    // 开始下载
    processDownloadQueue();
}

function processDownloadQueue() {
    while (currentBatchDownloads.length < maxConcurrentDownloads && batchQueue.length > 0) {
        const videoItem = batchQueue.shift();
        startSingleBatchDownload(videoItem);
    }
    
    updateDownloadQueue();
}

function startSingleBatchDownload(videoItem) {
    console.log('开始单个批量下载:', videoItem.url);
    const downloadId = Date.now() + Math.random();
    
    // 获取文件名并限制长度
    let fileName = videoItem.info.title || '未知文件';
    if (fileName === '准备下载...') {
        // 如果是默认标题，尝试从URL提取文件名
        const urlParts = videoItem.url.split('/');
        fileName = urlParts[urlParts.length - 1] || '未知文件';
    }
    
    // 限制文件名长度为20个字符
    const displayFileName = fileName.length > 20 ? fileName.substring(0, 20) + '...' : fileName;
    
    const downloadItem = {
        id: downloadId,
        url: videoItem.url,
        info: videoItem.info,
        progress: 0,
        status: `准备下载: ${displayFileName}`
    };
    
    currentBatchDownloads.push(downloadItem);
    updateCurrentDownloads();
    
    console.log('发送下载请求，batchId:', downloadId);
    // 构建搜索上下文，如果有关键字则创建类似搜索下载的上下文
    let searchContext = null;
    if (appState.downloadMode === 'search') {
        searchContext = appState.currentSearchContext;
    } else if (appState.batchKeyword) {
        // 为批量下载创建搜索上下文，使用关键字
        searchContext = {
            searchType: 'keyword',
            originalQuery: appState.batchKeyword
        };
    }
    
    // 发送下载请求
    socket.emit('download-video', {
        url: videoItem.url,
        quality: elements.qualitySelect.value,
        audioFormat: elements.audioFormatSelect.value,
        audioOnly: elements.audioOnlyCheckbox.checked,
        playlist: elements.playlistCheckbox.checked,
        subtitles: elements.subtitlesCheckbox.checked,
        subtitleLang: elements.subtitleLang.value,
        saveAsDirectory: elements.saveAsDirectorySelect.value === 'true',
        presetAlias: document.getElementById('presetAlias').value, // yt-dlp 2025.08.20 预设别名功能
        batchId: downloadId,
        searchContext: searchContext
    });
    console.log('下载请求已发送，searchContext:', searchContext);
}

function stopBatchDownload() {
    isBatchDownloading = false;
    batchQueue = [];
    
    elements.batchDownloadBtn.disabled = false;
    elements.stopBatchBtn.disabled = true;
    
    addBatchLog('批量下载已停止');
}

function updateOverallProgress() {
    const total = appState.batchVideoInfos.filter(item => item.valid).length;
    const completed = batchResults.length;
    const progress = total > 0 ? (completed / total) * 100 : 0;
    
    elements.overallProgressBar.style.width = `${progress}%`;
    elements.overallProgressText.textContent = `${completed}/${total} (${Math.round(progress)}%)`;
}

function updateCurrentDownloads() {
    elements.currentDownloadsContainer.innerHTML = '';
    
    currentBatchDownloads.forEach(download => {
        const element = document.createElement('div');
        element.className = 'current-download-item';
        element.innerHTML = `
            <div class="current-download-header">
                <div class="current-download-title">${download.info.title}</div>
                <div class="current-download-status">${download.status}</div>
            </div>
            <div class="progress-bar">
                <div class="progress-fill" style="width: ${download.progress}%"></div>
            </div>
        `;
        elements.currentDownloadsContainer.appendChild(element);
    });
}

function updateDownloadQueue() {
    const queueList = elements.downloadQueueContainer;
    queueList.innerHTML = '';
    
    batchQueue.forEach((item, index) => {
        const element = document.createElement('div');
        element.className = 'queue-item';
        const title = item.info ? item.info.title : (item.title || '未知标题');
        element.innerHTML = `
            <span class="queue-index">${index + 1}</span>
            <span>${title}</span>
        `;
        queueList.appendChild(element);
    });
}

function addBatchLog(message) {
    const logContainer = elements.batchLogContainer;
    const timestamp = new Date().toLocaleTimeString();
    const logEntry = document.createElement('div');
    
    // 根据消息类型设置不同的颜色
    let messageColor = '#333'; // 默认颜色
    let backgroundColor = 'transparent';
    let fontWeight = 'normal';
    
    if (message.includes('✅') || message.includes('下载完成') || message.includes('批量下载完成')) {
        // 成功消息 - 绿色
        messageColor = '#28a745';
        fontWeight = 'bold';
    } else if (message.includes('❌') || message.includes('下载失败') || message.includes('错误')) {
        // 错误消息 - 红色
        messageColor = '#dc3545';
        fontWeight = 'bold';
    } else if (message.includes('%') || message.includes('下载中')) {
        // 进度消息 - 蓝色
        messageColor = '#007bff';
    } else if (message.includes('🎉')) {
        // 完成消息 - 紫色背景
        messageColor = '#6f42c1';
        backgroundColor = '#f8f9ff';
        fontWeight = 'bold';
    } else if (message.includes('批量下载已停止')) {
        // 停止消息 - 橙色
        messageColor = '#fd7e14';
        fontWeight = 'bold';
    }
    
    logEntry.style.cssText = `
        padding: 4px 8px;
        margin: 2px 0;
        border-radius: 4px;
        background-color: ${backgroundColor};
        font-weight: ${fontWeight};
        transition: all 0.2s ease;
    `;
    
    logEntry.innerHTML = `
        <span style="color: #666; font-size: 0.85em;">[${timestamp}]</span> 
        <span style="color: ${messageColor};">${message}</span>
    `;
    
    logContainer.appendChild(logEntry);
    logContainer.scrollTop = logContainer.scrollHeight;
    
    // 添加闪烁效果突出重要消息
    if (message.includes('✅') || message.includes('❌') || message.includes('🎉')) {
        logEntry.style.animation = 'highlight 0.5s ease-in-out';
    }
}

// 设置 Socket.IO 监听器
function setupSocketListeners() {
    socket.on('download-progress', (data) => {
        if (data.batchId) {
            // 批量下载进度更新
            const downloadItem = currentBatchDownloads.find(item => item.id === data.batchId);
            if (downloadItem) {
                downloadItem.progress = data.progress;
                downloadItem.status = `下载中 ${data.progress}%`;
                updateCurrentDownloads();
                addBatchLog(`${downloadItem.info.title}: ${data.progress}%`);
            }
        } else {
            // 单个下载进度更新
            updateProgress(data.message);
        }
    });

    socket.on('download-complete', (data) => {
        if (data.batchId) {
            // 批量下载完成
            const downloadIndex = currentBatchDownloads.findIndex(item => item.id === data.batchId);
            if (downloadIndex !== -1) {
                const downloadItem = currentBatchDownloads[downloadIndex];
                currentBatchDownloads.splice(downloadIndex, 1);
                
                const resultItem = {
                    ...downloadItem,
                    success: true,
                    downloadPath: data.downloadPath,
                    title: data.title
                };
                
                batchResults.push(resultItem);
                console.log('添加成功结果到batchResults:', resultItem);
                console.log('当前batchResults长度:', batchResults.length);
                
                addBatchLog(`✅ 下载完成: ${data.title}`);
                updateOverallProgress();
                updateCurrentDownloads();
                
                // 继续处理队列
                if (isBatchDownloading) {
                    processDownloadQueue();
                }
                
                // 检查是否全部完成
                if (batchQueue.length === 0 && currentBatchDownloads.length === 0) {
                    completeBatchDownload();
                }
            }
        } else {
            // 单个下载完成
            completeDownload(data.message);
        }
    });

    socket.on('download-error', (data) => {
        if (data.batchId) {
            // 批量下载错误
            const downloadIndex = currentBatchDownloads.findIndex(item => item.id === data.batchId);
            if (downloadIndex !== -1) {
                const downloadItem = currentBatchDownloads[downloadIndex];
                currentBatchDownloads.splice(downloadIndex, 1);
                
                const resultItem = {
                    ...downloadItem,
                    success: false,
                    error: data.error
                };
                
                batchResults.push(resultItem);
                console.log('添加失败结果到batchResults:', resultItem);
                console.log('当前batchResults长度:', batchResults.length);
                
                addBatchLog(`❌ 下载失败: ${downloadItem.info.title} - ${data.error}`);
                updateOverallProgress();
                updateCurrentDownloads();
                
                // 继续处理队列
                if (isBatchDownloading) {
                    processDownloadQueue();
                }
                
                // 检查是否全部完成
                if (batchQueue.length === 0 && currentBatchDownloads.length === 0) {
                    completeBatchDownload();
                }
            }
        } else {
            // 单个下载错误
            showError(data.message);
        }
    });
}

function completeBatchDownload() {
    isBatchDownloading = false;
    
    // 添加调试信息
    console.log('批量下载完成，batchResults:', batchResults);
    
    const successCount = batchResults.filter(r => r.success === true).length;
    const failCount = batchResults.filter(r => r.success === false).length;
    const totalCount = batchResults.length;
    
    console.log(`统计信息 - 总数: ${totalCount}, 成功: ${successCount}, 失败: ${failCount}`);
    
    addBatchLog(`🎉 批量下载完成! 成功: ${successCount}, 失败: ${failCount}`);
    
    // 检查是否是搜索下载模式（通过检查selectedVideos数组是否有内容）
    const isSearchDownloadMode = appState.selectedVideos && appState.selectedVideos.length > 0;
    
    if (isSearchDownloadMode) {
        // 搜索下载模式的特殊处理
        elements.downloadSelectedBtn.disabled = false;
        elements.downloadSelectedBtn.textContent = '下载选中的视频';
        
        // 显示详细的结果统计列表
        showBatchDownloadResults(batchResults);
        
        // 清空选中的视频列表
        appState.selectedVideos = [];
        updateSelectedCount();
    } else {
        // 普通批量下载模式
        elements.batchDownloadBtn.disabled = false;
        elements.stopBatchBtn.disabled = true;
    }
    
    // 刷新下载列表
    loadDownloadsList();
}

// 显示批量下载结果统计列表
function showBatchDownloadResults(results) {
    // 创建结果统计模态框
    const modal = document.createElement('div');
    modal.className = 'batch-results-modal';
    modal.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 1000;
    `;
    
    const successCount = results.filter(r => r.success === true).length;
    const failCount = results.filter(r => r.success === false).length;
    const totalCount = results.length;
    
    modal.innerHTML = `
        <div class="modal-content" style="
            background: white;
            border-radius: 8px;
            padding: 0;
            max-width: 800px;
            max-height: 80vh;
            width: 90%;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
        ">
            <div class="modal-header" style="
                padding: 20px;
                border-bottom: 1px solid #e0e0e0;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
            ">
                <h3 style="margin: 0; display: flex; align-items: center;">
                    <i class="fas fa-chart-bar" style="margin-right: 10px;"></i>
                    批量下载结果统计
                </h3>
                <button class="modal-close" style="
                    position: absolute;
                    top: 15px;
                    right: 20px;
                    background: none;
                    border: none;
                    color: white;
                    font-size: 24px;
                    cursor: pointer;
                    padding: 0;
                    width: 30px;
                    height: 30px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 50%;
                    transition: background-color 0.2s;
                " onmouseover="this.style.backgroundColor='rgba(255,255,255,0.2)'" 
                   onmouseout="this.style.backgroundColor='transparent'">×</button>
            </div>
            <div class="modal-body" style="
                padding: 20px;
                max-height: 60vh;
                overflow-y: auto;
            ">
                <div class="results-summary" style="
                    display: grid;
                    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
                    gap: 15px;
                    margin-bottom: 20px;
                ">
                    <div class="summary-card" style="
                        background: #f8f9fa;
                        padding: 15px;
                        border-radius: 8px;
                        text-align: center;
                        border-left: 4px solid #007bff;
                    ">
                        <div style="font-size: 24px; font-weight: bold; color: #007bff;">${totalCount}</div>
                        <div style="color: #666; font-size: 14px;">总计</div>
                    </div>
                    <div class="summary-card" style="
                        background: #f8f9fa;
                        padding: 15px;
                        border-radius: 8px;
                        text-align: center;
                        border-left: 4px solid #28a745;
                    ">
                        <div style="font-size: 24px; font-weight: bold; color: #28a745;">${successCount}</div>
                        <div style="color: #666; font-size: 14px;">成功</div>
                    </div>
                    <div class="summary-card" style="
                        background: #f8f9fa;
                        padding: 15px;
                        border-radius: 8px;
                        text-align: center;
                        border-left: 4px solid #dc3545;
                    ">
                        <div style="font-size: 24px; font-weight: bold; color: #dc3545;">${failCount}</div>
                        <div style="color: #666; font-size: 14px;">失败</div>
                    </div>
                </div>
                
                <div class="results-list">
                    <h4 style="margin-bottom: 15px; color: #333;">
                        <i class="fas fa-list"></i> 详细结果
                    </h4>
                    ${results.map((result, index) => `
                        <div class="result-item" style="
                            display: flex;
                            align-items: center;
                            padding: 12px;
                            margin-bottom: 8px;
                            border-radius: 6px;
                            background: ${result.success ? '#d4edda' : '#f8d7da'};
                            border: 1px solid ${result.success ? '#c3e6cb' : '#f5c6cb'};
                        ">
                            <div class="result-icon" style="
                                margin-right: 12px;
                                font-size: 18px;
                                color: ${result.success ? '#28a745' : '#dc3545'};
                            ">
                                ${result.success ? '✅' : '❌'}
                            </div>
                            <div class="result-info" style="flex: 1;">
                                <div class="result-title" style="
                                    font-weight: bold;
                                    color: #333;
                                    margin-bottom: 4px;
                                ">${escapeHtml(result.info.title || '未知标题')}</div>
                                <div class="result-details" style="
                                    font-size: 12px;
                                    color: #666;
                                ">
                                    ${result.success 
                                        ? `下载成功 ${result.downloadPath ? `| 路径: ${result.downloadPath}` : ''}` 
                                        : `下载失败 | 错误: ${result.error || '未知错误'}`
                                    }
                                </div>
                            </div>
                        </div>
                    `).join('')}
                </div>
            </div>
            <div class="modal-footer" style="
                padding: 15px 20px;
                border-top: 1px solid #e0e0e0;
                background: #f8f9fa;
                text-align: right;
            ">
                <button class="btn btn-primary" style="
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    border: none;
                    padding: 8px 20px;
                    border-radius: 4px;
                    color: white;
                    cursor: pointer;
                    font-size: 14px;
                ">确定</button>
            </div>
        </div>
    `;
    
    // 添加事件监听器
    const closeBtn = modal.querySelector('.modal-close');
    const confirmBtn = modal.querySelector('.btn-primary');
    
    const closeModal = () => {
        document.body.removeChild(modal);
    };
    
    closeBtn.addEventListener('click', closeModal);
    confirmBtn.addEventListener('click', closeModal);
    
    // 点击背景关闭
    modal.addEventListener('click', (e) => {
        if (e.target === modal) {
            closeModal();
        }
    });
    
    // ESC键关闭
    const handleEsc = (e) => {
        if (e.key === 'Escape') {
            closeModal();
            document.removeEventListener('keydown', handleEsc);
        }
    };
    document.addEventListener('keydown', handleEsc);
    
    document.body.appendChild(modal);
    
    // 显示Toast消息
    showToast(`批量下载完成！成功: ${successCount}, 失败: ${failCount}`, 
              failCount > 0 ? 'warning' : 'success', 5000);
}

// 获取视频信息
async function getVideoInfo() {
    const url = elements.videoUrl.value.trim();
    if (!url) {
        showMessage('请输入视频 URL', 'error');
        return;
    }

    elements.getInfoBtn.disabled = true;
    elements.getInfoBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 获取中...';

    try {
        const response = await fetch('/api/info', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ url })
        });

        const data = await response.json();

        if (response.ok) {
            displayVideoInfo(data);
        } else {
            showMessage(data.error || '获取视频信息失败', 'error');
        }
    } catch (error) {
        showMessage('网络错误: ' + error.message, 'error');
    } finally {
        elements.getInfoBtn.disabled = false;
        elements.getInfoBtn.innerHTML = '<i class="fas fa-info-circle"></i> 获取信息';
    }
}

// 显示视频信息
function displayVideoInfo(info) {
    const duration = info.duration ? formatDuration(info.duration) : 'N/A';
    const viewCount = info.view_count ? info.view_count.toLocaleString() : 'N/A';
    const uploadDate = info.upload_date ? formatDate(info.upload_date) : 'N/A';
    
    // 新增字段的处理
    const orientation = info.orientation || 'N/A';
    const width = info.width || 'N/A';
    const height = info.height || 'N/A';
    const bitrate = info.bitrate ? `${info.bitrate} kbps` : 'N/A';

    elements.videoInfo.innerHTML = `
        <div class="video-info-card fade-in">
            <div class="video-info-item">
                <strong><i class="fas fa-video"></i> 标题:</strong>
                <span>${escapeHtml(info.title || 'N/A')}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-user"></i> 上传者:</strong>
                <span>${escapeHtml(info.uploader || 'N/A')}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-clock"></i> 时长:</strong>
                <span>${duration}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-eye"></i> 观看次数:</strong>
                <span>${viewCount}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-calendar"></i> 上传日期:</strong>
                <span>${uploadDate}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-mobile-alt"></i> 视频方向:</strong>
                <span>${orientation}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-expand-arrows-alt"></i> 帧宽度:</strong>
                <span>${width}px</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-arrows-alt-v"></i> 帧高度:</strong>
                <span>${height}px</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-tachometer-alt"></i> 比特率:</strong>
                <span>${bitrate}</span>
            </div>
            <div class="video-info-item">
                <strong><i class="fas fa-align-left"></i> 描述:</strong>
                <span>${escapeHtml((info.description || '').substring(0, 200))}${info.description && info.description.length > 200 ? '...' : ''}</span>
            </div>
        </div>
    `;

    elements.infoSection.style.display = 'block';
}

// 开始下载
function startDownload() {
    const url = elements.videoUrl.value.trim();
    if (!url) {
        showMessage('请输入视频 URL', 'error');
        return;
    }

    if (isDownloading) {
        showMessage('正在下载中，请等待完成', 'error');
        return;
    }

    isDownloading = true;
    elements.downloadBtn.disabled = true;
    elements.downloadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 下载中...';

    elements.progressSection.style.display = 'block';
    elements.progressFill.style.width = '0%';
    elements.progressText.textContent = '准备下载...';
    elements.logContainer.textContent = '';

    // 保存下载开始状态到状态管理器
    if (typeof globalStateManager !== 'undefined') {
        globalStateManager.updateModuleState('download', {
            isDownloading: true,
            downloadLogs: ['准备下载...']
        });
    }

    // 通过 Socket.IO 开始下载
    socket.emit('download-video', {
        url: url,
        quality: elements.qualitySelect.value,
        audioFormat: elements.audioFormatSelect.value,
        audioOnly: elements.audioOnlyCheckbox.checked,
        playlist: elements.playlistCheckbox.checked,
        subtitles: elements.subtitlesCheckbox.checked,
        subtitleLang: elements.subtitleLang.value,
        saveAsDirectory: elements.saveAsDirectorySelect.value === 'true',
        presetAlias: document.getElementById('presetAlias').value, // yt-dlp 2025.08.20 预设别名功能
        searchContext: appState.downloadMode === 'search' ? appState.currentSearchContext : null
    });
}

// 更新下载进度
function updateProgress(message) {
    elements.logContainer.textContent += message + '\n';
    elements.logContainer.scrollTop = elements.logContainer.scrollHeight;

    // 尝试解析进度信息
    if (message.includes('%')) {
        const match = message.match(/(\d+(?:\.\d+)?)%/);
        if (match) {
            const percentage = parseFloat(match[1]);
            elements.progressFill.style.width = percentage + '%';
            elements.progressText.textContent = `下载进度: ${percentage}%`;
        }
    }

    if (message.includes('Downloading') || message.includes('下载')) {
        elements.progressText.textContent = '正在下载...';
    }

    // 保存下载进度状态到状态管理器
    if (typeof globalStateManager !== 'undefined') {
        globalStateManager.updateModuleState('download', {
            isDownloading: isDownloading,
            downloadLogs: globalStateManager.getDownloadLogs(),
            currentProgress: elements.progressText.textContent
        });
    }
}

// 完成下载
function completeDownload(data) {
    isDownloading = false;
    elements.downloadBtn.disabled = false;
    elements.downloadBtn.innerHTML = '<i class="fas fa-download"></i> 开始下载';

    elements.progressFill.style.width = '100%';
    elements.progressText.textContent = '下载完成!';
    
    let message = data.message || '下载完成!';
    if (data.downloadPath) {
        message += `\n📁 保存路径: ${data.downloadPath}`;
    }
    if (data.videoTitle) {
        message += `\n🎬 视频标题: ${data.videoTitle}`;
    }
    
    elements.logContainer.textContent += '\n✅ ' + message + '\n';
    elements.logContainer.scrollTop = elements.logContainer.scrollHeight;

    // 保存下载完成状态到状态管理器
    if (typeof globalStateManager !== 'undefined') {
        globalStateManager.updateModuleState('download', {
            isDownloading: false,
            downloadLogs: globalStateManager.getDownloadLogs()
        });
    }

    showMessage('下载完成!', 'success');
    loadDownloadsList();
}

// 显示错误
function showError(message) {
    isDownloading = false;
    elements.downloadBtn.disabled = false;
    elements.downloadBtn.innerHTML = '<i class="fas fa-download"></i> 开始下载';

    elements.logContainer.textContent += '\n❌ 错误: ' + message + '\n';
    elements.logContainer.scrollTop = elements.logContainer.scrollHeight;

    showMessage('下载失败: ' + message, 'error');
}

// 加载下载列表
async function loadDownloadsList() {
    try {
        // 获取当前选择的展示模式
        const displayMode = document.querySelector('input[name="displayMode"]:checked').value;
        
        let response, data;
        
        if (displayMode === 'search') {
            // 按搜索关键字分组
            response = await fetch('/api/downloads/by-search');
            data = await response.json();
            displayFilesBySearch(data);
        } else {
            // 按日期分组、平铺显示或表格显示
            response = await fetch('/api/downloads');
            data = await response.json();
            
            if (displayMode === 'flat') {
                displayFilesFlat(data);
            } else if (displayMode === 'table') {
                displayFilesTable(data);
            } else {
                displayFilesByDate(data);
            }
        }
    } catch (error) {
        console.error('加载下载列表失败:', error);
        elements.downloadsList.innerHTML = '<p class="no-downloads">❌ 加载失败</p>';
    }
}

// 按搜索关键字分组显示文件
function displayFilesBySearch(data) {
    if (!data.filesBySearch || Object.keys(data.filesBySearch).length === 0) {
        elements.downloadsList.innerHTML = '<p class="no-downloads">📭 暂无通过搜索下载的文件</p>';
        return;
    }
    
    let html = '';
    
    // 显示总文件数
    html += `<div class="downloads-summary">
        <h3>🔍 按搜索关键字分组 (${data.totalKeywords} 个关键字，共 ${data.totalFiles} 个文件)</h3>
    </div>`;
    
    // 按搜索关键字分组显示
    const sortedKeywords = Object.keys(data.filesBySearch).sort((a, b) => {
        // 按创建时间倒序排列
        const timeA = new Date(data.filesBySearch[a].createdAt);
        const timeB = new Date(data.filesBySearch[b].createdAt);
        return timeB - timeA;
    });
    
    for (const originalKeyword of sortedKeywords) {
        const searchGroup = data.filesBySearch[originalKeyword];
        const files = searchGroup.files;
        
        // 确保files是一个数组
        if (!Array.isArray(files)) {
            console.warn(`搜索关键字 ${originalKeyword} 的文件列表不是数组:`, files);
            continue;
        }
        
        if (files.length === 0) continue;
        
        // 计算总文件大小
        const totalSize = files.reduce((sum, file) => sum + file.size, 0);
        
        html += `
            <div class="search-group">
                <h4 class="search-header" onclick="toggleSearchGroup(this)">
                    <span>🔍 "${escapeHtml(originalKeyword)}" (${files.length} 个文件, ${formatFileSize(totalSize)})</span>
                    <span class="toggle-icon">▼</span>
                </h4>
                <div class="search-content">
                    <div class="search-info">
                        <small>📁 文件夹名: ${escapeHtml(searchGroup.sanitizedKeyword)} | 🕒 首次搜索: ${formatDateTime(searchGroup.createdAt)}</small>
                    </div>
        `;
        
        // 按下载时间分组显示文件
        const filesByDate = {};
        files.forEach(file => {
            const downloadDate = file.downloadedAt.split('T')[0]; // 提取日期部分
            if (!filesByDate[downloadDate]) {
                filesByDate[downloadDate] = [];
            }
            filesByDate[downloadDate].push(file);
        });
        
        const sortedDates = Object.keys(filesByDate).sort().reverse();
        
        for (const date of sortedDates) {
            const dateFiles = filesByDate[date];
            
            html += `
                <div class="date-subgroup">
                    <h5 class="date-subheader" onclick="toggleDateSubgroup(this)">
                        <span>📅 ${date} (${dateFiles.length} 个文件)</span>
                        <span class="toggle-icon">▼</span>
                    </h5>
                    <div class="date-subcontent">
            `;
            
            for (const file of dateFiles) {
                const fileIcon = getFileIcon(file.title);
                
                html += `
                    <div class="download-item fade-in">
                        <div class="download-info">
                            <div class="file-header">
                                <span class="file-icon">${fileIcon}</span>
                                <h6 class="file-name">${escapeHtml(file.title)}</h6>
                            </div>
                            <div class="file-details">
                                <span class="file-size">📊 ${formatFileSize(file.size)}</span>
                                <span class="file-date">🕒 ${formatDateTime(file.downloadedAt)}</span>
                                ${file.exists ? '<span class="file-status">✅ 存在</span>' : '<span class="file-status">❌ 已删除</span>'}
                            </div>
                            <div class="file-path">
                                <small>📍 ${escapeHtml(file.path)}</small>
                            </div>
                        </div>
                        <div class="download-actions">
                            <button class="btn btn-sm btn-danger delete-file-btn" 
                                    onclick="deleteDownloadFile('${escapeHtml(file.path)}')" 
                                    title="删除文件">
                                <i class="fas fa-trash"></i>
                            </button>
                        </div>
                    </div>
                `;
            }
            
            html += `
                    </div>
                </div>
            `;
        }
        
        html += `
                </div>
            </div>
        `;
    }
    
    elements.downloadsList.innerHTML = html;
}

// 平铺显示文件
function displayFilesFlat(data) {
    if (!data.files || !Array.isArray(data.files) || data.files.length === 0) {
        elements.downloadsList.innerHTML = '<p class="no-downloads">📭 暂无下载文件</p>';
        return;
    }
    
    let html = '';
    
    // 显示总文件数
    html += `<div class="downloads-summary">
        <h3>📄 平铺显示 (共 ${data.totalFiles} 个文件)</h3>
    </div>`;
    
    // 按修改时间倒序排列
    const sortedFiles = data.files.sort((a, b) => new Date(b.created) - new Date(a.created));
    
    html += sortedFiles.map(file => {
        const fileIcon = getFileIcon(file.name);
        let durationInfo = '';
        
        // 如果是视频文件且有时长信息，显示时长
        if (file.isVideo && file.duration) {
            durationInfo = `<span class="file-duration">⏱️ ${formatDuration(file.duration)}</span>`;
        }
        
        // 添加视频详细信息
        let videoInfo = '';
        if (file.isVideo && (file.bitrate || file.orientation || file.width || file.height)) {
            const bitrateText = file.bitrate ? `📊 ${formatBitrate(file.bitrate)}` : '';
            const orientationText = file.orientation ? `📐 ${formatOrientation(file.orientation)}` : '';
            const resolutionText = (file.width && file.height) ? `📺 ${formatResolution(file.width, file.height)}` : '';
            
            const videoDetails = [bitrateText, orientationText, resolutionText].filter(Boolean).join(' ');
            if (videoDetails) {
                videoInfo = `<div class="video-details">${videoDetails}</div>`;
            }
        }
        
        return `
            <div class="download-item fade-in">
                <div class="download-info">
                    <div class="file-header">
                        <span class="file-icon">${fileIcon}</span>
                        <h6 class="file-name">${escapeHtml(file.name)}</h6>
                    </div>
                    <div class="file-details">
                        <span class="file-size">📊 ${formatFileSize(file.size)}</span>
                        <span class="file-date">🕒 ${formatDateTime(file.created)}</span>
                        ${durationInfo}
                    </div>
                    ${videoInfo}
                    <div class="file-path">
                        <small>📍 ${escapeHtml(file.relativePath)}</small>
                    </div>
                </div>
                <div class="download-actions">
                    <button class="btn btn-sm btn-danger delete-file-btn" 
                            onclick="deleteDownloadFile('${escapeHtml(file.relativePath)}')" 
                            title="删除文件">
                        <i class="fas fa-trash"></i>
                    </button>
                </div>
            </div>
        `;
    }).join('');
    
    elements.downloadsList.innerHTML = html;
}

// 按日期分组显示文件（原有功能）
function displayFilesByDate(data) {
    if (!data.files || !Array.isArray(data.files) || data.files.length === 0) {
        elements.downloadsList.innerHTML = '<p class="no-downloads">📭 暂无下载文件</p>';
        return;
    }
    
    let html = '';
    
    // 显示总文件数
    html += `<div class="downloads-summary">
        <h3>📁 按日期分组 (共 ${data.totalFiles} 个文件)</h3>
    </div>`;
    
    // 按日期分组显示
    if (data.filesByDate && Object.keys(data.filesByDate).length > 0) {
        const sortedDates = Object.keys(data.filesByDate).sort().reverse(); // 最新日期在前
        
        // 获取当前下载的文件夹名称
        const currentDownloadFolders = getCurrentDownloadFolders();
        
        for (const date of sortedDates) {
            const dateFiles = data.filesByDate[date];
            const isToday = isDateToday(date);
            const hasCurrentDownload = Object.keys(dateFiles).some(folderName => 
                currentDownloadFolders.includes(folderName)
            );
            
            // 今天的日期或包含当前下载的日期默认展开，其他收起
            const dateCollapsed = !isToday && !hasCurrentDownload;
            
            html += `
                <div class="date-group">
                    <h4 class="date-header ${dateCollapsed ? 'collapsed' : ''}" onclick="toggleDateGroup(this)">
                        <span>📅 ${date} (${Object.keys(dateFiles).length} 个文件夹)</span>
                        <span class="toggle-icon">▼</span>
                    </h4>
                    <div class="date-content ${dateCollapsed ? 'collapsed' : ''}">
            `;
            
            // 按文件名分组显示
            for (const [folderName, files] of Object.entries(dateFiles)) {
                // 确保files是一个数组
                if (!Array.isArray(files)) {
                    console.warn(`文件夹 ${folderName} 的文件列表不是数组:`, files);
                    continue;
                }
                
                const isCurrentDownload = currentDownloadFolders.includes(folderName);
                // 当前下载的文件夹默认展开，其他收起
                const folderCollapsed = !isCurrentDownload;
                
                html += `
                    <div class="folder-group ${isCurrentDownload ? 'current-download' : ''}">
                        <h5 class="folder-header ${folderCollapsed ? 'collapsed' : ''}" onclick="toggleFolderGroup(this)">
                            <span>📂 ${escapeHtml(folderName)} (${files.length} 个文件)</span>
                            <span class="toggle-icon">▼</span>
                        </h5>

                        <div class="folder-content ${folderCollapsed ? 'collapsed' : ''}">
                `;
                
                for (const file of files) {
                    const fileIcon = getFileIcon(file.name);
                    let durationInfo = '';
                    
                    // 如果是视频文件且有时长信息，显示时长
                    if (file.isVideo && file.duration) {
                        durationInfo = `<span class="file-duration">⏱️ ${formatDuration(file.duration)}</span>`;
                    }
                    
                    // 添加视频详细信息
                    let videoInfo = '';
                    if (file.isVideo && (file.bitrate || file.orientation || file.width || file.height)) {
                        const bitrateText = file.bitrate ? `📊 ${formatBitrate(file.bitrate)}` : '';
                        const orientationText = file.orientation ? `📐 ${formatOrientation(file.orientation)}` : '';
                        const resolutionText = (file.width && file.height) ? `📺 ${formatResolution(file.width, file.height)}` : '';
                        
                        const videoDetails = [bitrateText, orientationText, resolutionText].filter(Boolean).join(' ');
                        if (videoDetails) {
                            videoInfo = `<div class="video-details">${videoDetails}</div>`;
                        }
                    }
                    
                    html += `
                        <div class="download-item fade-in">
                            <div class="download-info">
                                <div class="file-header">
                                    <span class="file-icon">${fileIcon}</span>
                                    <h6 class="file-name">${escapeHtml(file.name)}</h6>
                                </div>
                                <div class="file-details">
                                    <span class="file-size">📊 ${formatFileSize(file.size)}</span>
                                    <span class="file-date">🕒 ${formatDateTime(file.created)}</span>
                                    ${durationInfo}
                                </div>
                                ${videoInfo}
                                <div class="file-path">
                                    <small>📍 ${escapeHtml(file.relativePath)}</small>
                                </div>
                            </div>
                            <div class="download-actions">
                                <button class="btn btn-sm btn-danger delete-file-btn" 
                                        onclick="deleteDownloadFile(event, '${escapeHtml(file.relativePath)}')" 
                                        title="删除文件">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </div>
                        </div>
                    `;
                }
                
                html += `
                        </div>
                    </div>
                `;
            }
            
            html += `
                    </div>
                </div>
            `;
        }
    } else {
        // 如果没有按日期分组的数据，显示简单列表
        html += data.files.map(file => {
            const fileIcon = getFileIcon(file.name);
            let durationInfo = '';
            
            // 如果是视频文件且有时长信息，显示时长
            if (file.isVideo && file.duration) {
                durationInfo = `<p>⏱️ 时长: ${formatDuration(file.duration)}</p>`;
            }
            
            // 添加视频详细信息
            let videoInfo = '';
            if (file.isVideo && (file.bitrate || file.orientation || file.width || file.height)) {
                const bitrateText = file.bitrate ? `比特率: ${formatBitrate(file.bitrate)}` : '';
                const orientationText = file.orientation ? `方向: ${formatOrientation(file.orientation)}` : '';
                const resolutionText = (file.width && file.height) ? `分辨率: ${formatResolution(file.width, file.height)}` : '';
                
                const videoDetails = [bitrateText, orientationText, resolutionText].filter(Boolean);
                if (videoDetails.length > 0) {
                    videoInfo = videoDetails.map(detail => `<p>📺 ${detail}</p>`).join('');
                }
            }
            
            return `
                <div class="download-item fade-in">
                    <div class="download-info">
                        <div class="file-header">
                            <span class="file-icon">${fileIcon}</span>
                            <h4>${escapeHtml(file.name)}</h4>
                        </div>
                        <p>📊 大小: ${formatFileSize(file.size)}</p>
                        <p>🕒 创建: ${formatDateTime(file.created)}</p>
                        ${durationInfo}
                        ${videoInfo}
                    </div>
                </div>
            `;
        }).join('');
    }
    
    elements.downloadsList.innerHTML = html;
}

// 表格显示文件
function displayFilesTable(data) {
    if (!data.files || !Array.isArray(data.files) || data.files.length === 0) {
        elements.downloadsList.innerHTML = '<p class="no-downloads">📭 暂无下载文件</p>';
        return;
    }
    
    let html = '';
    
    // 显示总文件数
    html += `<div class="downloads-summary">
        <h3>📊 表格显示 (共 ${data.totalFiles} 个文件)</h3>
    </div>`;
    
    // 按修改时间倒序排列
    const sortedFiles = data.files.sort((a, b) => new Date(b.created) - new Date(a.created));
    
    // 创建表格
    html += `
        <div class="files-table-container">
            <table class="files-table">
                <thead>
                    <tr>
                        <th>名称</th>
                        <th>日期</th>
                        <th>类型</th>
                        <th>大小</th>
                        <th>时长</th>
                        <th>视频方向</th>
                        <th>帧宽度</th>
                        <th>帧高度</th>
                        <th>比特率</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
    `;
    
    sortedFiles.forEach(file => {
        const fileIcon = getFileIcon(file.name);
        const fileExt = file.name.split('.').pop().toUpperCase();
        
        // 格式化视频信息
        const duration = file.isVideo && file.duration ? formatDuration(file.duration) : '-';
        const orientation = file.isVideo && file.orientation ? formatOrientation(file.orientation) : '-';
        const width = file.isVideo && file.width ? file.width : '-';
        const height = file.isVideo && file.height ? file.height : '-';
        const bitrate = file.isVideo && file.bitrate ? formatBitrate(file.bitrate) : '-';
        
        html += `
            <tr class="file-row">
                <td class="file-name-cell">
                    <div class="file-name-content">
                        <span class="file-icon">${fileIcon}</span>
                        <span class="file-name" title="${escapeHtml(file.name)}">${escapeHtml(file.name)}</span>
                    </div>
                    <div class="file-path-small">
                        <small>📍 ${escapeHtml(file.relativePath)}</small>
                    </div>
                </td>
                <td class="file-date">${formatDateTime(file.created)}</td>
                <td class="file-type">${fileExt} 文件</td>
                <td class="file-size">${formatFileSize(file.size)}</td>
                <td class="file-duration">${duration}</td>
                <td class="file-orientation">${orientation}</td>
                <td class="file-width">${width}</td>
                <td class="file-height">${height}</td>
                <td class="file-bitrate">${bitrate}</td>
                <td class="file-actions">
                    <button class="btn btn-sm btn-danger delete-file-btn" 
                            onclick="deleteDownloadFile('${escapeHtml(file.relativePath)}')" 
                            title="删除文件">
                        <i class="fas fa-trash"></i>
                    </button>
                </td>
            </tr>
        `;
    });
    
    html += `
                </tbody>
            </table>
        </div>
    `;
    
    elements.downloadsList.innerHTML = html;
}

// 获取当前下载的文件夹名称
function getCurrentDownloadFolders() {
    const folders = [];
    
    // 从当前批量下载中获取文件夹名称
    currentBatchDownloads.forEach(download => {
        if (download.info && download.info.title) {
            folders.push(download.info.title);
        }
    });
    
    // 从下载队列中获取文件夹名称
    batchQueue.forEach(item => {
        if (item.info && item.info.title) {
            folders.push(item.info.title);
        }
    });
    
    return [...new Set(folders)]; // 去重
}

// 判断日期是否为今天
function isDateToday(dateString) {
    const today = new Date();
    const todayString = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
    return dateString === todayString;
}

// 切换日期组的展开/收起状态
function toggleDateGroup(headerElement) {
    const dateContent = headerElement.nextElementSibling;
    const toggleIcon = headerElement.querySelector('.toggle-icon');
    
    headerElement.classList.toggle('collapsed');
    dateContent.classList.toggle('collapsed');
    
    // 更新图标
    if (headerElement.classList.contains('collapsed')) {
        toggleIcon.textContent = '▶';
    } else {
        toggleIcon.textContent = '▼';
    }
}

// 切换文件夹组的展开/收起状态
function toggleFolderGroup(headerElement) {
    // 找到folder-content元素，它在folder-actions之后
    const folderActions = headerElement.nextElementSibling;
    const folderContent = folderActions.nextElementSibling;
    const toggleIcon = headerElement.querySelector('.toggle-icon');
    
    headerElement.classList.toggle('collapsed');
    folderContent.classList.toggle('collapsed');
    
    // 更新图标
    if (headerElement.classList.contains('collapsed')) {
        toggleIcon.textContent = '▶';
    } else {
        toggleIcon.textContent = '▼';
    }
}

// 切换搜索分组的展开/收起状态
function toggleSearchGroup(headerElement) {
    const searchContent = headerElement.nextElementSibling;
    const toggleIcon = headerElement.querySelector('.toggle-icon');
    
    headerElement.classList.toggle('collapsed');
    searchContent.classList.toggle('collapsed');
    
    // 更新图标
    if (headerElement.classList.contains('collapsed')) {
        toggleIcon.textContent = '▶';
    } else {
        toggleIcon.textContent = '▼';
    }
}

// 切换日期子分组的展开/收起状态
function toggleDateSubgroup(headerElement) {
    const dateSubContent = headerElement.nextElementSibling;
    const toggleIcon = headerElement.querySelector('.toggle-icon');
    
    headerElement.classList.toggle('collapsed');
    dateSubContent.classList.toggle('collapsed');
    
    // 更新图标
    if (headerElement.classList.contains('collapsed')) {
        toggleIcon.textContent = '▶';
    } else {
        toggleIcon.textContent = '▼';
    }
}

// 获取文件图标
function getFileIcon(filename) {
    const ext = filename.split('.').pop().toLowerCase();
    switch (ext) {
        case 'mp4':
        case 'avi':
        case 'mkv':
        case 'mov':
        case 'wmv':
        case 'flv':
        case 'webm':
            return '🎬';
        case 'mp3':
        case 'wav':
        case 'flac':
        case 'aac':
        case 'm4a':
        case 'ogg':
            return '🎵';
        case 'jpg':
        case 'jpeg':
        case 'png':
        case 'gif':
        case 'bmp':
        case 'webp':
            return '🖼️';
        case 'srt':
        case 'vtt':
        case 'ass':
        case 'ssa':
            return '📝';
        case 'json':
            return '📄';
        default:
            return '📄';
    }
}

// 格式化日期时间
function formatDateTime(dateString) {
    const date = new Date(dateString);
    return date.toLocaleString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit'
    });
}

// 气泡提示系统
class ToastManager {
    constructor() {
        this.container = null;
        this.toasts = new Map();
        this.init();
    }

    init() {
        // 创建提示容器
        this.container = document.createElement('div');
        this.container.className = 'toast-container';
        document.body.appendChild(this.container);
    }

    show(message, type = 'info', duration = 5000) {
        const toast = this.createToast(message, type, duration);
        this.container.appendChild(toast);
        
        // 触发显示动画
        requestAnimationFrame(() => {
            toast.classList.add('show');
        });

        // 自动隐藏
        if (duration > 0) {
            const progressBar = toast.querySelector('.toast-progress');
            if (progressBar) {
                progressBar.style.width = '100%';
                progressBar.style.transitionDuration = duration + 'ms';
                
                requestAnimationFrame(() => {
                    progressBar.style.width = '0%';
                });
            }

            setTimeout(() => {
                this.hide(toast);
            }, duration);
        }

        return toast;
    }

    createToast(message, type, duration) {
        const toast = document.createElement('div');
        toast.className = `toast ${type}`;
        
        const icons = {
            success: '✓',
            error: '✕',
            warning: '!',
            info: 'i'
        };

        toast.innerHTML = `
            <div class="toast-icon">${icons[type] || icons.info}</div>
            <div class="toast-content">${this.escapeHtml(message)}</div>
            <button class="toast-close" onclick="toastManager.hide(this.parentElement)">×</button>
            ${duration > 0 ? '<div class="toast-progress"></div>' : ''}
        `;

        return toast;
    }

    hide(toast) {
        if (!toast || !toast.parentElement) return;
        
        toast.classList.remove('show');
        toast.classList.add('hide');
        
        setTimeout(() => {
            if (toast.parentElement) {
                toast.parentElement.removeChild(toast);
            }
        }, 300);
    }

    escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    // 便捷方法
    success(message, duration = 5000) {
        return this.show(message, 'success', duration);
    }

    error(message, duration = 7000) {
        return this.show(message, 'error', duration);
    }

    warning(message, duration = 6000) {
        return this.show(message, 'warning', duration);
    }

    info(message, duration = 5000) {
        return this.show(message, 'info', duration);
    }
}

// 创建全局提示管理器
const toastManager = new ToastManager();

// 更新 showMessage 函数使用新的气泡提示
function showMessage(message, type = 'info') {
    // 兼容旧的类型名称
    const typeMap = {
        'error': 'error',
        'success': 'success',
        'info': 'info',
        'warning': 'warning'
    };
    
    const toastType = typeMap[type] || 'info';
    toastManager.show(message, toastType);
}

// 新的提示函数，用于替换 alert
// 注意：showToast函数在文件后面有更完整的定义

// 工具函数
function formatDuration(seconds) {
    // 确保输入是数字并四舍五入到整数
    const totalSeconds = Math.round(parseFloat(seconds));
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const secs = totalSeconds % 60;

    if (hours > 0) {
        return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    } else {
        return `${minutes}:${secs.toString().padStart(2, '0')}`;
    }
}

// 格式化比特率
function formatBitrate(bitrate) {
    if (!bitrate) return 'N/A';
    const bitrateNum = parseInt(bitrate);
    if (bitrateNum >= 1000000) {
        return `${(bitrateNum / 1000000).toFixed(1)} Mbps`;
    } else if (bitrateNum >= 1000) {
        return `${(bitrateNum / 1000).toFixed(0)} Kbps`;
    }
    return `${bitrateNum} bps`;
}

// 格式化视频方向
function formatOrientation(orientation) {
    if (!orientation) return 'N/A';
    const orientationMap = {
        'landscape': '横向',
        'portrait': '竖向',
        'square': '正方形'
    };
    return orientationMap[orientation] || orientation;
}

// 格式化视频分辨率
function formatResolution(width, height) {
    if (!width || !height) return 'N/A';
    return `${width}×${height}`;
}

function formatDate(dateString) {
    if (!dateString) return 'N/A';
    const year = dateString.substring(0, 4);
    const month = dateString.substring(4, 6);
    const day = dateString.substring(6, 8);
    return `${year}-${month}-${day}`;
}

function formatFileSize(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// ==================== 搜索下载功能 ====================

// 执行搜索
async function performSearch() {
    const keyword = elements.searchKeyword.value.trim();
    if (!keyword) {
        showToast('请输入搜索关键字', 'warning');
        return;
    }
    
    const maxResults = parseInt(elements.maxResults.value);
    const sortBy = elements.sortBy.value;
    const searchType = elements.searchType.value;
    const videoType = elements.videoType.value;
    
    // 禁用搜索按钮并显示加载状态
    elements.searchBtn.disabled = true;
    elements.searchBtn.textContent = '搜索中...';
    
    try {
        const response = await fetch('/api/search', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                query: keyword,
                maxResults,
                sortBy,
                searchType,
                videoType
            })
        });
        
        const data = await response.json();
        
        if (response.ok && data.success) {
            appState.searchResults = data.results;
            appState.selectedVideos = [];
            
            // 保存搜索上下文
            appState.currentSearchContext = {
                searchType: searchType,
                hashtag: searchType === 'hashtag' ? keyword.replace(/^#/, '') : null,
                keyword: searchType === 'keyword' ? keyword : null,
                originalQuery: keyword,  // 保存原始查询关键字
                videoType: videoType
            };
            
            // 重置分页状态
            appState.searchPagination.currentPage = 1;
            appState.searchPagination.totalItems = data.results.length;
            appState.searchPagination.totalPages = Math.ceil(data.results.length / appState.searchPagination.itemsPerPage);
            
            displaySearchResultsWithPagination(keyword, searchType, videoType);
            
            // 根据搜索类型显示不同的成功消息
            let searchTypeText = '';
            if (searchType === 'hashtag') {
                searchTypeText = '标签';
            } else {
                searchTypeText = '关键字';
            }
            
            let videoTypeText = '';
            if (videoType === 'shorts') {
                videoTypeText = '短视频';
            } else if (videoType === 'long') {
                videoTypeText = '长视频';
            } else {
                videoTypeText = '视频';
            }
            
            showToast(`找到 ${data.results.length} 个${videoTypeText} (${searchTypeText}搜索)`, 'success');
        } else {
            showToast(data.error || '搜索失败', 'error');
        }
    } catch (error) {
        console.error('搜索错误:', error);
        showToast('搜索失败: ' + error.message, 'error');
    } finally {
        elements.searchBtn.disabled = false;
        elements.searchBtn.textContent = '搜索';
    }
}

// 创建搜索结果项
function createSearchResultItem(video, index) {
    const item = document.createElement('div');
    item.className = 'search-result-item';
    
    // 格式化时长
    const formatDuration = (seconds) => {
        if (!seconds) return '未知';
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = seconds % 60;
        
        if (hours > 0) {
            return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        } else {
            return `${minutes}:${secs.toString().padStart(2, '0')}`;
        }
    };
    
    // 格式化观看次数
    const formatViewCount = (count) => {
        if (!count) return '未知';
        if (count >= 1000000) {
            return (count / 1000000).toFixed(1) + 'M';
        } else if (count >= 1000) {
            return (count / 1000).toFixed(1) + 'K';
        }
        return count.toString();
    };
    
    item.innerHTML = `
        <div class="search-result-checkbox">
            <input type="checkbox" id="video-${index}" data-index="${index}">
        </div>
        <div class="search-result-thumbnail">
            <img src="${video.thumbnail || '/placeholder-thumbnail.jpg'}" 
                 alt="${video.title}" 
                 onerror="this.src='/placeholder-thumbnail.jpg'">
            <div class="duration-overlay">${formatDuration(video.duration)}</div>
        </div>
        <div class="search-result-info">
            <h3 class="video-title">${video.title || '未知标题'}</h3>
            <div class="video-meta">
                <span class="uploader">${video.uploader || '未知上传者'}</span>
                <span class="view-count">${formatViewCount(video.view_count)} 次观看</span>
            </div>
            <div class="video-url">
                <a href="${video.url}" target="_blank" rel="noopener noreferrer">
                    ${video.url}
                </a>
            </div>
        </div>
    `;
    
    // 添加复选框事件监听
    const checkbox = item.querySelector('input[type="checkbox"]');
    checkbox.addEventListener('change', (e) => {
        handleVideoSelection(parseInt(e.target.dataset.index));
    });
    
    return item;
}

// 显示搜索结果（支持分页）
function displaySearchResultsWithPagination(keyword, searchType = 'keyword', videoType = 'all') {
    elements.searchResults.style.display = 'block';
    
    // 构建搜索结果标题
    let searchTypeText = searchType === 'hashtag' ? '标签' : '关键字';
    let videoTypeText = '';
    if (videoType === 'shorts') {
        videoTypeText = ' - 短视频';
    } else if (videoType === 'long') {
        videoTypeText = ' - 长视频';
    }
    
    elements.searchResultsHeader.querySelector('h3').textContent = 
        `"${keyword}" 的搜索结果 (${appState.searchResults.length} 个视频) - ${searchTypeText}搜索${videoTypeText}`;
    
    // 重置选择状态
    elements.selectAllCheckbox.checked = false;
    elements.selectedCount.textContent = '0';
    elements.downloadSelectedBtn.disabled = true;
    
    displaySearchResults();
}

// 处理视频选择
function handleVideoSelection(index) {
    const { currentPage, itemsPerPage } = appState.searchPagination;
    const actualIndex = (currentPage - 1) * itemsPerPage + index;
    const checkbox = document.getElementById(`video-${index}`);
    const video = appState.searchResults[actualIndex];
    
    if (checkbox.checked) {
        if (!appState.selectedVideos.find(v => v.id === video.id)) {
            appState.selectedVideos.push(video);
        }
    } else {
        appState.selectedVideos = appState.selectedVideos.filter(v => v.id !== video.id);
    }
    
    updateSelectedCount();
}

// 处理全选/取消全选
function handleSelectAll() {
    const selectAll = elements.selectAllCheckbox.checked;
    
    // 更新当前页面所有复选框状态
    const checkboxes = elements.searchResultsList.querySelectorAll('input[type="checkbox"]');
    checkboxes.forEach((checkbox, index) => {
        checkbox.checked = selectAll;
        handleVideoSelection(index);
    });
}

// 更新选择计数
function updateSelectedCount() {
    const selectedCount = appState.selectedVideos.length;
    const totalCount = appState.searchResults.length;
    
    elements.selectedCount.textContent = selectedCount;
    elements.downloadSelectedBtn.disabled = selectedCount === 0;
    
    // 更新全选复选框状态
    const { currentPage, itemsPerPage } = appState.searchPagination;
    const startIndex = (currentPage - 1) * itemsPerPage;
    const endIndex = startIndex + itemsPerPage;
    const pageResults = appState.searchResults.slice(startIndex, endIndex);
    
    const selectedInPage = pageResults.filter(video => 
        appState.selectedVideos.find(v => v.id === video.id)
    ).length;
    
    if (selectedInPage === 0) {
        elements.selectAllCheckbox.indeterminate = false;
        elements.selectAllCheckbox.checked = false;
    } else if (selectedInPage === pageResults.length) {
        elements.selectAllCheckbox.indeterminate = false;
        elements.selectAllCheckbox.checked = true;
    } else {
        elements.selectAllCheckbox.indeterminate = true;
    }
}

// 下载选中的视频
async function downloadSelectedVideos() {
    if (appState.selectedVideos.length === 0) {
        showToast('请先选择要下载的视频', 'warning');
        return;
    }
    
    // 获取同时下载数设置
    const concurrentCount = elements.searchConcurrentSelect ? 
        parseInt(elements.searchConcurrentSelect.value) : 5;
    
    // 确认下载
    const confirmTitle = '确认下载';
    const confirmMessage = `确定要下载 ${appState.selectedVideos.length} 个视频吗？\n同时下载数: ${concurrentCount}`;
    const confirmed = await showCustomConfirm(confirmTitle, confirmMessage);
    
    if (!confirmed) {
        return;
    }
    
    // 禁用下载按钮
    elements.downloadSelectedBtn.disabled = true;
    elements.downloadSelectedBtn.textContent = `下载中... (0/${appState.selectedVideos.length})`;
    
    // 设置批量下载状态
    isBatchDownloading = true;
    maxConcurrentDownloads = concurrentCount;
    
    batchQueue = appState.selectedVideos.map((video, index) => ({
        url: video.url,
        info: {
            title: video.title || '未知标题',
            uploader: video.uploader || '未知上传者',
            duration: video.duration || 0,
            view_count: video.view_count || 0
        },
        index: index + 1,
        total: appState.selectedVideos.length
    }));
    currentBatchDownloads = [];
    batchResults = [];
    
    // 显示批量进度区域
    elements.batchProgressSection.style.display = 'block';
    elements.overallProgressBar.style.width = '0%';
    elements.overallProgressText.textContent = `准备下载 ${appState.selectedVideos.length} 个视频...`;
    elements.currentDownloadsContainer.innerHTML = '';
    elements.downloadQueueContainer.innerHTML = '';
    
    // 更新总体进度的计算基础
    appState.batchVideoInfos = appState.selectedVideos.map(video => ({
        valid: true,
        title: video.title
    }));
    
    // 开始下载
    processDownloadQueue();
    
    showToast(`开始下载 ${appState.selectedVideos.length} 个视频`, 'info');
}

// 清空搜索结果
function clearSearchResults() {
    appState.searchResults = [];
    appState.selectedVideos = [];
    elements.searchResults.style.display = 'none';
    elements.searchKeyword.value = '';
    elements.searchResultsList.innerHTML = '';
    elements.batchProgressSection.style.display = 'none';
    
    // 重置按钮状态
    elements.downloadSelectedBtn.disabled = true;
    elements.downloadSelectedBtn.textContent = '下载选中的视频';
    
    showToast('已清空搜索结果', 'info');
}

// 格式化观看次数
function formatViewCount(count) {
    if (!count || count === 0) return '0';
    
    if (count >= 1000000) {
        return (count / 1000000).toFixed(1) + 'M';
    } else if (count >= 1000) {
        return (count / 1000).toFixed(1) + 'K';
    }
    return count.toString();
}

// 格式化上传日期
function formatUploadDate(dateStr) {
    if (!dateStr) return '未知';
    
    try {
        // yt-dlp 返回的日期格式通常是 YYYYMMDD
        const year = dateStr.substring(0, 4);
        const month = dateStr.substring(4, 6);
        const day = dateStr.substring(6, 8);
        
        const date = new Date(year, month - 1, day);
        return date.toLocaleDateString('zh-CN');
    } catch (error) {
        return dateStr;
    }
}

// ==================== 视频处理功能 ====================

// 背景图管理器
const backgroundImageManager = {
    selectedImages: [],
    
    // 显示背景图选择器 - 使用操作系统的文件选择对话框
    showImageSelector() {
        // 创建一个隐藏的文件输入元素
        let fileInput = document.getElementById('backgroundImageFileInput');
        
        // 如果元素不存在，创建一个新的
        if (!fileInput) {
            fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.id = 'backgroundImageFileInput';
            fileInput.accept = 'image/*'; // 只接受图片文件
            fileInput.multiple = true; // 允许选择多个文件
            fileInput.style.display = 'none'; // 隐藏元素
            document.body.appendChild(fileInput);
            
            // 添加文件选择事件监听器
            fileInput.addEventListener('change', (event) => {
                const files = event.target.files;
                if (files && files.length > 0) {
                    this.handleSelectedFiles(files);
                }
            });
        }
        
        // 触发文件选择对话框
        fileInput.click();
    },
    
    // 处理用户选择的文件
    async handleSelectedFiles(files) {
        if (files.length === 0) return;
        
        // 限制最多选择2张背景图
        const maxImages = 2;
        const filesToProcess = Array.from(files).slice(0, maxImages);
        
        if (files.length > maxImages) {
            toastManager.warning(`最多只能选择${maxImages}张背景图，已自动选择前${maxImages}张`);
        }
        
        // 显示上传进度提示
        const uploadToast = toastManager.info('正在上传图片...', 0); // 0 表示不自动隐藏
        
        try {
            // 创建 FormData 对象
            const formData = new FormData();
            
            // 添加所有选择的图片文件
            filesToProcess.forEach(file => {
                if (file.type.startsWith('image/')) {
                    formData.append('images', file);
                }
            });
            
            // 上传到服务器
            const response = await fetch('/api/upload-images', {
                method: 'POST',
                body: formData
            });
            
            const result = await response.json();
            
            if (response.ok && result.success) {
                // 清空之前的选择
                this.selectedImages = [];
                
                // 添加上传成功的图片到选中列表
                result.files.forEach(file => {
                    this.selectedImages.push({
                        name: file.originalName,
                        path: file.path, // 服务器路径
                        size: file.size,
                        type: file.type,
                        serverPath: file.fullPath, // 服务器完整路径
                        uploaded: true // 标记为已上传
                    });
                });
                
                // 更新UI显示选中的图片
                this.updateSelectedImagesDisplay();
                
                // 隐藏上传进度提示
                toastManager.hide(uploadToast);
                
                // 显示成功消息
                toastManager.success(`成功上传并选择 ${this.selectedImages.length} 张背景图片`);
            } else {
                throw new Error(result.error || '上传失败');
            }
        } catch (error) {
            console.error('上传图片失败:', error);
            
            // 隐藏上传进度提示
            toastManager.hide(uploadToast);
            
            // 显示错误消息
            toastManager.error('上传图片失败: ' + error.message);
            
            // 如果上传失败，回退到本地预览模式
            this.handleSelectedFilesLocal(filesToProcess);
        }
    },
    
    // 本地文件处理（作为上传失败的回退方案）
    handleSelectedFilesLocal(files) {
        // 限制最多选择2张背景图
        const maxImages = 2;
        const filesToProcess = Array.from(files).slice(0, maxImages);
        
        if (files.length > maxImages) {
            toastManager.warning(`最多只能选择${maxImages}张背景图，已自动选择前${maxImages}张`);
        }
        
        // 清空之前的选择
        this.selectedImages = [];
        
        // 处理每个选择的文件
        filesToProcess.forEach(file => {
            // 检查是否为图片文件
            if (file.type.startsWith('image/')) {
                // 创建文件的URL
                const fileUrl = URL.createObjectURL(file);
                
                // 添加到选中的图片列表
                this.selectedImages.push({
                    name: file.name,
                    path: fileUrl,
                    size: file.size,
                    type: file.type,
                    file: file, // 保存文件对象以便后续处理
                    uploaded: false // 标记为未上传
                });
            }
        });
        
        // 更新UI显示选中的图片
        this.updateSelectedImagesDisplay();
        
        // 显示警告消息
        toastManager.warning(`图片上传失败，已切换到本地预览模式。选择了 ${this.selectedImages.length} 张背景图片`);
    },
    
    // 更新选中图片的显示
    updateSelectedImagesDisplay() {
        const container = document.getElementById('selectedBackgroundImages');
        if (!container) return;
        
        if (this.selectedImages.length === 0) {
            container.innerHTML = `
                <div class="background-images-placeholder">
                    <i class="fas fa-plus-circle"></i>
                    <span>点击下方"添加背景图"按钮选择背景图片</span>
                </div>
            `;
            return;
        } else {
            container.innerHTML = this.selectedImages.map((image, index) => {
                // 处理不同的数据格式（兼容新旧格式）
                const imageName = typeof image === 'object' ? image.name : image;
                const imagePath = typeof image === 'object' ? image.path : `/uploads/${image}`;
                
                return `
                <div class="selected-image-item">
                    <div class="image-order">${index + 1}</div>
                    <div class="image-preview-small">
                        <img src="${imagePath}" alt="${imageName}" onerror="this.src=''" />
                    </div>
                    <div class="image-info">
                        <span class="image-name">${imageName}</span>
                    </div>
                    <div class="image-actions">
                        <button type="button" class="btn btn-sm btn-outline-secondary" onclick="backgroundImageManager.moveImageUp(${index})" ${index === 0 ? 'disabled' : ''}>
                            <i class="fas fa-arrow-up"></i>
                        </button>
                        <button type="button" class="btn btn-sm btn-outline-secondary" onclick="backgroundImageManager.moveImageDown(${index})" ${index === this.selectedImages.length - 1 ? 'disabled' : ''}>
                            <i class="fas fa-arrow-down"></i>
                        </button>
                        <button type="button" class="btn btn-sm btn-outline-danger" onclick="backgroundImageManager.removeImage(${index})">
                            <i class="fas fa-times"></i>
                        </button>
                    </div>
                </div>
                `;
            }).join('');
        }
    },
    
    // 从服务器数据生成图片列表
    generateImageListFromData(images) {
        return images.map(image => `
            <div class="image-select-item" data-filename="${image.name}">
                <div class="image-preview">
                    <img src="${image.path}" alt="${image.name}" onerror="this.src=''" />
                </div>
                <div class="image-info">
                    <div class="image-name">${image.name}</div>
                    <div class="image-meta">${this.formatFileSize(image.size)} • ${this.formatDate(image.date)}</div>
                </div>
                <div class="image-select-checkbox">
                    <input type="checkbox" onchange="backgroundImageManager.toggleImageSelection('${image.name}', this.checked)">
                </div>
            </div>
        `).join('');
    },
    
    // 格式化文件大小
    formatFileSize(bytes) {
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },
    
    // 格式化日期
    formatDate(dateString) {
        if (!dateString) return '未知时间';
        const date = new Date(dateString);
        return date.toLocaleDateString('zh-CN');
    },
    
    // 切换图片选择状态
    toggleImageSelection(filename, selected) {
        if (selected) {
            if (!this.selectedImages.includes(filename)) {
                this.selectedImages.push(filename);
            }
        } else {
            const index = this.selectedImages.indexOf(filename);
            if (index > -1) {
                this.selectedImages.splice(index, 1);
            }
        }
    },
    
    // 全选图片
    selectAllImages() {
        const checkboxes = document.querySelectorAll('#backgroundImageList input[type="checkbox"]');
        checkboxes.forEach(checkbox => {
            checkbox.checked = true;
            const filename = checkbox.closest('.image-select-item').dataset.filename;
            if (!this.selectedImages.includes(filename)) {
                this.selectedImages.push(filename);
            }
        });
    },
    
    // 清空选择
    clearSelection() {
        this.selectedImages = [];
        const checkboxes = document.querySelectorAll('#backgroundImageList input[type="checkbox"]');
        checkboxes.forEach(checkbox => {
            checkbox.checked = false;
        });
    },
    
    // 过滤图片
    filterImages() {
        const searchInput = document.getElementById('imageSearchInput');
        const imageList = document.getElementById('backgroundImageList');
        
        if (!searchInput || !imageList) return;
        
        const searchTerm = searchInput.value.toLowerCase();
        const imageItems = imageList.querySelectorAll('.image-select-item');
        
        imageItems.forEach(item => {
            const imageName = item.querySelector('.image-name');
            if (!imageName) return;
            
            const nameMatch = imageName.textContent.toLowerCase().includes(searchTerm);
            item.style.display = nameMatch ? 'flex' : 'none';
        });
    },
    
    // 确认选择
    confirmSelection() {
        if (this.selectedImages.length === 0) {
            showToast('请至少选择一张背景图片', 'warning');
            return;
        }
        
        this.updateSelectedImagesDisplay();
        document.getElementById('backgroundImageSelectModal').remove();
        showToast(`已选择 ${this.selectedImages.length} 张背景图片`, 'success');
    },
    

    
    // 移除图片
    removeImage(index) {
        if (index >= 0 && index < this.selectedImages.length) {
            const image = this.selectedImages[index];
            
            // 如果是对象且未上传到服务器（本地 blob URL），需要释放URL资源
            if (typeof image === 'object' && image.path && !image.uploaded) {
                try {
                    URL.revokeObjectURL(image.path);
                } catch (e) {
                    console.warn('释放URL资源失败:', e);
                }
            }
            
            this.selectedImages.splice(index, 1);
            this.updateSelectedImagesDisplay();
        }
    },
    
    // 上移图片
    moveImageUp(index) {
        if (index > 0) {
            [this.selectedImages[index], this.selectedImages[index - 1]] = 
            [this.selectedImages[index - 1], this.selectedImages[index]];
            this.updateSelectedImagesDisplay();
        }
    },
    
    // 下移图片
    moveImageDown(index) {
        if (index < this.selectedImages.length - 1) {
            [this.selectedImages[index], this.selectedImages[index + 1]] = 
            [this.selectedImages[index + 1], this.selectedImages[index]];
            this.updateSelectedImagesDisplay();
        }
    },
    
    // 清空图片
    clearImages() {
        // 释放所有本地对象URL资源
        this.selectedImages.forEach(image => {
            if (typeof image === 'object' && image.path && !image.uploaded) {
                try {
                    URL.revokeObjectURL(image.path);
                } catch (e) {
                    console.warn('释放URL资源失败:', e);
                }
            }
        });
        
        this.selectedImages = [];
        this.updateSelectedImagesDisplay();
    },
    
    // 获取选中的图片
    getSelectedImages() {
        // 如果是对象数组，提取路径信息
        if (this.selectedImages.length > 0 && typeof this.selectedImages[0] === 'object') {
            return this.selectedImages.map(image => {
                // 如果图片已上传到服务器，返回服务器完整路径
                if (image.uploaded && image.serverPath) {
                    return image.serverPath;
                }
                // 否则返回相对路径（用于本地预览模式）
                return image.path;
            });
        }
        // 如果是字符串数组，直接返回
        return this.selectedImages;
    }
};

// 视频处理功能对象
const videoProcessing = {
    workflow: [],
    currentWorkflowId: null,
    
    // 初始化视频处理功能
    init() {
        this.setupEventListeners();
        this.loadDownloadedFiles();
    },
    
    // 设置事件监听器
    setupEventListeners() {
        // 操作按钮 - 使用事件委托
        document.querySelector('.operation-buttons').addEventListener('click', (e) => {
            if (e.target.closest('button[data-operation]')) {
                const operation = e.target.closest('button[data-operation]').dataset.operation;
                this.showOperationModal(operation);
            }
        });
        
        // 工作流控制
        const clearWorkflowBtn = document.getElementById('clearWorkflowBtn');
        const startProcessingBtn = document.getElementById('startProcessingBtn');
        const refreshProcessingFilesBtn = document.getElementById('refreshProcessingFilesBtn');
        
        if (clearWorkflowBtn) {
            clearWorkflowBtn.addEventListener('click', () => this.clearWorkflow());
        }
        if (startProcessingBtn) {
            startProcessingBtn.addEventListener('click', () => this.startProcessing());
        }
        if (refreshProcessingFilesBtn) {
            refreshProcessingFilesBtn.addEventListener('click', () => this.loadDownloadedFiles());
        }
    },
    
    // 加载已下载的文件
    async loadDownloadedFiles() {
        try {
            const response = await fetch('/api/downloads');
            const data = await response.json();
            
            const availableFiles = document.getElementById('availableFiles');
            
            if (data.files && data.files.length > 0) {
                // 过滤视频文件
                const videoFiles = data.files.filter(file => {
                    const ext = file.name.split('.').pop().toLowerCase();
                    return ['mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'webm'].includes(ext);
                });
                
                if (videoFiles.length > 0) {
                    let html = '';
                    videoFiles.forEach(file => {
                        let durationInfo = '';
                        
                        // 如果是视频文件且有时长信息，显示时长
                        if (file.isVideo && file.duration) {
                            durationInfo = ` | 时长: ${formatDuration(file.duration)}`;
                        }
                        
                        // 添加视频详细信息
                        let videoInfo = '';
                        if (file.isVideo && (file.bitrate || file.orientation || file.width || file.height)) {
                            const bitrateText = file.bitrate ? `比特率: ${formatBitrate(file.bitrate)}` : '';
                            const orientationText = file.orientation ? `方向: ${formatOrientation(file.orientation)}` : '';
                            const resolutionText = (file.width && file.height) ? `分辨率: ${formatResolution(file.width, file.height)}` : '';
                            
                            const videoDetails = [bitrateText, orientationText, resolutionText].filter(Boolean).join(' | ');
                            if (videoDetails) {
                                videoInfo = ` | ${videoDetails}`;
                            }
                        }
                        
                        html += `
                            <div class="file-item">
                                <div class="file-info">
                                    <div class="file-name">${escapeHtml(file.name)}</div>
                                    <div class="file-details">
                                        大小: ${formatFileSize(file.size)} | 
                                        创建: ${formatDateTime(file.created)}${durationInfo}${videoInfo}
                                    </div>
                                </div>
                            </div>
                        `;
                    });
                    availableFiles.innerHTML = html;
                } else {
                    availableFiles.innerHTML = '<p class="no-files">没有找到视频文件</p>';
                }
            } else {
                availableFiles.innerHTML = '<p class="no-files">暂无下载文件</p>';
            }
            
            // 更新按钮状态
            this.updateButtonStates();
        } catch (error) {
            console.error('加载文件列表失败:', error);
            const availableFiles = document.getElementById('availableFiles');
            if (availableFiles) {
                availableFiles.innerHTML = '<p class="no-files">加载失败</p>';
            }
        }
    },
    
    // 更新按钮状态
    updateButtonStates() {
        const hasWorkflow = this.workflow.length > 0;
        const availableFiles = this.getAvailableVideoFiles();
        const hasAvailableFiles = availableFiles.length > 0;
        
        // 操作按钮 - 只要有可用文件就可以添加操作
        document.querySelectorAll('.operation-buttons .btn').forEach(btn => {
            btn.disabled = !hasAvailableFiles;
        });
        
        // 工作流控制按钮
        document.getElementById('clearWorkflowBtn').disabled = !hasWorkflow;
        document.getElementById('startProcessingBtn').disabled = !hasWorkflow;
    },
    
    // 显示操作模态框
    showOperationModal(operationType) {
        const modal = this.createOperationModal(operationType);
        document.body.appendChild(modal);
        
        // 如果是合并操作，初始化合并文件选择器
        if (operationType === 'merge') {
            setTimeout(() => {
                // 使用新的合并文件管理器
                mergeFileManager.updateSelectedFilesDisplay();
            }, 100);
        }
        
        // 如果是马赛克操作，初始化事件监听器
        if (operationType === 'mosaic') {
            setTimeout(() => {
                this.initMosaicModalEvents(modal);
            }, 100);
        }
    },
    
    // 创建操作模态框
    createOperationModal(operationType) {
        const modal = document.createElement('div');
        modal.className = 'operation-modal';
        
        const operationNames = {
            trim: '截取视频',
            scale: '缩放视频',
            watermark: '添加水印',
            mosaic: '添加马赛克',
            merge: '合并视频',
            background: '添加背景图',
            backgroundVideo: '添加背景视频',
            split: '分割视频',
            mirror: '镜像翻转',
            rotate: '旋转视频',
            speed: '调整速度',
            subtitle: '添加字幕'
        };
        
        modal.innerHTML = `
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title">${operationNames[operationType]}</h3>
                    <button class="modal-close" onclick="this.closest('.operation-modal').remove()">×</button>
                </div>
                <div class="modal-body">
                    ${this.getOperationForm(operationType)}
                </div>
                <div class="modal-footer">
                    <button class="btn btn-secondary" onclick="this.closest('.operation-modal').remove()">取消</button>
                    <button class="btn btn-primary" onclick="videoProcessing.addOperation('${operationType}', this.closest('.operation-modal'))">添加</button>
                </div>
            </div>
        `;
        
        return modal;
    },

    // 初始化马赛克模态框事件
    initMosaicModalEvents(modal) {
        // 监听马赛克区域设置模式的改变
        const mosaicSelectionModeRadios = modal.querySelectorAll('input[name="mosaicSelectionMode"]');
        mosaicSelectionModeRadios.forEach(radio => {
            radio.addEventListener('change', () => {
                this.updatePreviewButtonState(modal);
            });
        });

        // 监听文件选择的改变
        this.initFileOptionEvents(modal);
        
        // 初始化预览按钮状态
        this.updatePreviewButtonState(modal);
    },

    // 更新预览按钮状态
    updatePreviewButtonState(modal) {
        const previewBtn = modal.querySelector('#previewVideoBtn');
        if (!previewBtn) return;

        // 检查是否选择了视频文件
        const selectedFileInfo = modal.querySelector('.selected-file-info');
        const hasSelectedFile = selectedFileInfo && selectedFileInfo.textContent.trim();
        
        // 检查是否选择了"在预览图上选择"模式
        const visualModeRadio = modal.querySelector('input[name="mosaicSelectionMode"][value="visual"]');
        const isVisualMode = visualModeRadio && visualModeRadio.checked;
        
        // 检查选择的文件是否为视频文件
        const filename = hasSelectedFile ? selectedFileInfo.textContent.trim() : '';
        const isVideoFile = filename && this.isVideoFile(filename);

        // 只有在选择了视频文件且选择了"在预览图上选择"模式时才启用按钮
        const shouldEnable = hasSelectedFile && isVideoFile && isVisualMode;
        
        previewBtn.disabled = !shouldEnable;
        
        if (shouldEnable) {
            previewBtn.innerHTML = '<i class="fas fa-eye"></i> 预览视频';
        } else {
            previewBtn.innerHTML = '<i class="fas fa-eye"></i> 预览视频';
        }
    },

    // 获取操作表单
    getOperationForm(operationType) {
        // 生成可选择的文件列表
        const fileOptions = this.generateFileOptions(operationType);
        
        switch (operationType) {
            case 'trim':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>开始时间 (格式: 00:01:30)</label>
                        <input type="text" id="startTime" placeholder="00:00:00" required>
                    </div>
                    <div class="param-group">
                        <label>结束时间 (格式: 00:02:30)</label>
                        <input type="text" id="endTime" placeholder="00:01:00" required>
                    </div>
                `;
            case 'scale':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>缩放方式</label>
                        <select id="scaleMode" onchange="videoProcessing.toggleScaleMode(this.value)">
                            <option value="dimensions">指定尺寸</option>
                            <option value="ratio">缩放比例</option>
                        </select>
                    </div>
                    <div id="dimensionsMode">
                        <div class="param-group">
                            <label>宽度</label>
                            <input type="number" id="width" placeholder="1920" min="1">
                        </div>
                        <div class="param-group">
                            <label>高度</label>
                            <input type="number" id="height" placeholder="1080" min="1">
                        </div>
                        <div class="param-group">
                            <label>
                                <input type="checkbox" id="keepAspectRatio" checked>
                                保持宽高比
                            </label>
                        </div>
                    </div>
                    <div id="ratioMode" style="display: none;">
                        <div class="param-group">
                            <label>缩放比例</label>
                            <input type="number" id="scaleRatio" placeholder="1.0" min="0.1" max="10" step="0.1" value="1.0">
                            <small>1.0 = 原始大小，2.0 = 放大2倍，0.5 = 缩小一半</small>
                        </div>
                    </div>
                `;
            case 'watermark':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>水印类型</label>
                        <select id="watermarkType" onchange="videoProcessing.toggleWatermarkType(this.value)">
                            <option value="text">文字水印</option>
                            <option value="image">图片水印</option>
                        </select>
                    </div>
                    <div id="textWatermark">
                        <div class="param-group">
                            <label>水印文字</label>
                            <input type="text" id="text" placeholder="水印文字" required>
                        </div>
                        <div class="param-group">
                            <label>字体大小</label>
                            <input type="number" id="fontSize" value="24" min="8" max="100">
                        </div>
                        <div class="param-group">
                            <label>颜色</label>
                            <input type="color" id="color" value="#ffffff">
                        </div>
                    </div>
                    <div id="imageWatermark" style="display: none;">
                        <div class="param-group">
                            <label>水印图片路径</label>
                            <input type="text" id="imagePath" placeholder="请输入图片文件路径" required>
                            <small>支持相对路径或绝对路径，如: watermark.png</small>
                        </div>
                        <div class="param-group">
                            <label>图片透明度</label>
                            <input type="range" id="imageOpacity" min="0" max="1" step="0.1" value="0.8" oninput="document.getElementById('opacityValue').textContent = this.value">
                            <span id="opacityValue">0.8</span>
                        </div>
                        <div class="param-group">
                            <label>图片大小 (相对于视频宽度的比例)</label>
                            <input type="number" id="imageScale" value="0.2" min="0.05" max="1" step="0.05">
                            <small>0.2 = 图片宽度为视频宽度的20%</small>
                        </div>
                    </div>
                    <div class="param-group">
                        <label>位置</label>
                        <select id="position">
                            <option value="top-left">左上角</option>
                            <option value="top-right">右上角</option>
                            <option value="bottom-left">左下角</option>
                            <option value="bottom-right">右下角</option>
                            <option value="center">居中</option>
                        </select>
                    </div>
                    <div class="param-group">
                        <label>边距 (像素)</label>
                        <input type="number" id="margin" value="20" min="0" max="200">
                    </div>
                `;
            case 'mosaic':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>视频预览</label>
                        <div style="margin-bottom: 15px;">
                            <button type="button" id="previewVideoBtn" class="btn btn-primary" onclick="videoProcessing.manualPreviewVideo()" disabled>
                                <i class="fas fa-eye"></i> 预览视频
                            </button>
                            <small style="margin-left: 10px; color: #666;">请先选择视频文件，然后点击预览按钮</small>
                        </div>
                        <div id="videoPreviewContainer" style="margin-bottom: 20px;">
                            <div class="video-preview-placeholder" style="
                                width: 100%;
                                height: 300px;
                                background: #f5f5f5;
                                border: 2px dashed #ddd;
                                display: flex;
                                align-items: center;
                                justify-content: center;
                                border-radius: 8px;
                                color: #666;
                                font-size: 14px;
                            ">
                                <div style="text-align: center;">
                                    <i class="fas fa-video" style="font-size: 48px; margin-bottom: 10px; opacity: 0.5;"></i>
                                    <p>点击"预览视频"按钮查看视频预览图</p>
                                    <p style="font-size: 12px; color: #999;">您可以在预览图上直接选择马赛克区域</p>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="param-group">
                        <label>马赛克区域设置</label>
                        <div class="mosaic-selection-mode" style="margin-bottom: 15px;">
                            <label style="display: flex; align-items: center; margin-bottom: 10px;">
                                <input type="radio" name="mosaicSelectionMode" value="manual" checked style="margin-right: 8px;">
                                手动输入坐标
                            </label>
                            <label style="display: flex; align-items: center; margin-bottom: 10px;">
                                <input type="radio" name="mosaicSelectionMode" value="visual" style="margin-right: 8px;">
                                在预览图上选择 <small style="color: #666; margin-left: 8px;">(需要先选择视频文件)</small>
                            </label>
                        </div>
                        <div class="mosaic-processing-mode" style="margin-bottom: 15px; padding: 12px; background: #f8f9fa; border-radius: 6px; border: 1px solid #e0e0e0;">
                            <label style="font-weight: 600; margin-bottom: 8px; display: block;">马赛克处理方式</label>
                            <label style="display: flex; align-items: center; margin-bottom: 8px;">
                                <input type="radio" name="mosaicProcessingMode" value="pixel-based" checked style="margin-right: 8px;">
                                <span>基于像素处理</span>
                                <small style="color: #666; margin-left: 8px;">只对选择区域中的非透明像素进行马赛克化</small>
                            </label>
                            <label style="display: flex; align-items: center;">
                                <input type="radio" name="mosaicProcessingMode" value="area-based" style="margin-right: 8px;">
                                <span>基于区域处理</span>
                                <small style="color: #666; margin-left: 8px;">对选择的整个区域进行马赛克覆盖</small>
                            </label>
                        </div>
                        <div id="mosaicRegions">
                            <div class="mosaic-region" data-region-id="0">
                                <h4>区域 1</h4>
                                <div class="region-params">
                                    <div class="param-row">
                                        <div class="param-col">
                                            <label>距离顶部 (像素)</label>
                                            <input type="number" name="top" value="50" min="0" required>
                                        </div>
                                        <div class="param-col">
                                            <label>距离底部 (像素)</label>
                                            <input type="number" name="bottom" value="50" min="0" required>
                                        </div>
                                    </div>
                                    <div class="param-row">
                                        <div class="param-col">
                                            <label>距离左侧 (像素)</label>
                                            <input type="number" name="left" value="50" min="0" required>
                                        </div>
                                        <div class="param-col">
                                            <label>距离右侧 (像素)</label>
                                            <input type="number" name="right" value="50" min="0" required>
                                        </div>
                                    </div>
                                    <div class="param-row">
                                        <div class="param-col">
                                            <label>马赛克厚度</label>
                                            <input type="number" name="thickness" value="20" min="5" max="100" required>
                                            <small>数值越大，马赛克块越大</small>
                                        </div>
                                    </div>
                                </div>
                                <button type="button" class="btn btn-danger btn-sm remove-region" onclick="videoProcessing.removeMosaicRegion(0)" style="display: none;">删除区域</button>
                            </div>
                        </div>
                        <button type="button" class="btn btn-secondary btn-sm" onclick="videoProcessing.addMosaicRegion()">添加区域</button>
                    </div>
                    <div class="param-group">
                        <label>时间段设置</label>
                        <div id="mosaicTimeSegments">
                            <div class="time-segment" data-segment-id="0">
                                <h4>时间段 1</h4>
                                <div class="param-row">
                                    <div class="param-col">
                                        <label>开始时间 (格式: 00:01:30)</label>
                                        <input type="text" name="start" placeholder="00:00:00" pattern="[0-9]{2}:[0-9]{2}:[0-9]{2}">
                                        <small>留空表示从视频开始</small>
                                    </div>
                                    <div class="param-col">
                                        <label>结束时间 (格式: 00:02:30)</label>
                                        <input type="text" name="end" placeholder="00:01:00" pattern="[0-9]{2}:[0-9]{2}:[0-9]{2}">
                                        <small>留空表示到视频结束</small>
                                    </div>
                                </div>
                                <button type="button" class="btn btn-danger btn-sm remove-segment" onclick="videoProcessing.removeMosaicTimeSegment(0)" style="display: none;">删除时间段</button>
                            </div>
                        </div>
                        <button type="button" class="btn btn-secondary btn-sm" onclick="videoProcessing.addMosaicTimeSegment()">添加时间段</button>
                        <small>如果不设置任何时间段，马赛克将应用于整个视频</small>
                    </div>
                `;
            case 'merge':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>要合并的文件</label>
                        <div class="merge-files-container">
                            <div class="selected-merge-files" id="selectedMergeFiles">
                                <div class="merge-files-placeholder">
                                    <i class="fas fa-plus-circle"></i>
                                    <span>点击下方"添加文件"按钮选择要合并的文件</span>
                                </div>
                            </div>
                            <div class="merge-files-actions">
                                <button type="button" class="btn btn-secondary btn-sm" onclick="try { console.log('按钮被点击'); videoProcessing.showMergeFileSelector(); } catch(e) { console.error('错误:', e); alert('错误: ' + e.message); }">
                                    <i class="fas fa-plus"></i> 添加文件
                                </button>
                                <button type="button" class="btn btn-outline-secondary btn-sm" onclick="videoProcessing.clearMergeFiles()">
                                    <i class="fas fa-trash"></i> 清空
                                </button>
                            </div>
                        </div>
                        <small>请选择至少2个文件进行合并，文件将按添加顺序合并</small>
                    </div>
                    <div class="param-group">
                        <label>合并方式</label>
                        <select id="mergeType">
                            <option value="concat">顺序拼接</option>
                            <option value="side-by-side">左右并排</option>
                            <option value="top-bottom">上下排列</option>
                        </select>
                    </div>
                `;
            case 'mirror':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>镜像方向</label>
                        <select id="direction">
                            <option value="horizontal">水平镜像</option>
                            <option value="vertical">垂直镜像</option>
                        </select>
                    </div>
                `;
            case 'rotate':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>旋转角度</label>
                        <select id="angle">
                            <option value="90">顺时针 90°</option>
                            <option value="180">180°</option>
                            <option value="270">逆时针 90°</option>
                        </select>
                    </div>
                `;
            case 'speed':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>速度倍数</label>
                        <input type="number" id="factor" value="1.0" min="0.1" max="10" step="0.1" required>
                    </div>
                    <p><small>1.0 = 正常速度，2.0 = 2倍速，0.5 = 0.5倍速</small></p>
                `;
            case 'background':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>背景图片</label>
                        <div class="background-images-container">
                            <div class="selected-background-images" id="selectedBackgroundImages">
                                <div class="background-images-placeholder">
                                    <i class="fas fa-plus-circle"></i>
                                    <span>点击下方"添加背景图"按钮选择背景图片</span>
                                </div>
                            </div>
                            <div class="background-images-actions">
                                <button type="button" class="btn btn-secondary btn-sm" onclick="videoProcessing.showBackgroundImageSelector()">
                                    <i class="fas fa-plus"></i> 添加背景图
                                </button>
                                <button type="button" class="btn btn-outline-secondary btn-sm" onclick="videoProcessing.clearBackgroundImages()">
                                    <i class="fas fa-trash"></i> 清空
                                </button>
                            </div>
                        </div>
                        <small>最多支持2张背景图片，系统将根据图片数量和视频时长自动决定切换方式</small>
                    </div>
                    <div class="param-group">
                        <label>背景图缩放模式</label>
                        <select id="scaleMode">
                            <option value="fill">填充整个画面</option>
                            <option value="fit">适应画面大小</option>
                            <option value="stretch">拉伸填充</option>
                        </select>
                        <small>
                            填充：保持比例，可能裁剪部分内容<br>
                            适应：保持比例，可能有黑边<br>
                            拉伸：强制填充，可能变形
                        </small>
                    </div>
                `;
            case 'backgroundVideo':
                return `
                    <div class="param-group">
                        <label>选择背景视频文件</label>
                        <div class="file-selector" id="backgroundVideoSelector">
                            <div class="file-selector-header">
                                <div class="file-search-container">
                                    <input type="text" class="file-search-input" placeholder="搜索视频文件..." 
                                           onkeyup="videoProcessing.filterBackgroundVideoFiles(this.value)">
                                    <i class="fas fa-search"></i>
                                </div>
                            </div>
                            <div class="file-selector-body" id="backgroundVideoList">
                                ${this.generateVideoFileOptions()}
                            </div>
                            <div class="file-selector-footer">
                                <div class="selected-file-info">
                                    <span class="selected-text">未选择背景视频</span>
                                </div>
                            </div>
                        </div>
                        <input type="hidden" id="backgroundVideoFile" value="" required>
                        <small>选择一个视频文件作为背景视频</small>
                    </div>
                    <div class="param-group">
                        <label>选择音频文件</label>
                        <div class="file-selector" id="audioFileSelector">
                            <div class="file-selector-header">
                                <div class="file-search-container">
                                    <input type="text" class="file-search-input" placeholder="搜索音频文件..." 
                                           onkeyup="videoProcessing.filterAudioFiles(this.value)">
                                    <i class="fas fa-search"></i>
                                </div>
                            </div>
                            <div class="file-selector-body" id="audioFileList">
                                ${this.generateAudioFileOptions()}
                            </div>
                            <div class="file-selector-footer">
                                <div class="selected-file-info">
                                    <span class="selected-text">未选择音频文件</span>
                                </div>
                            </div>
                        </div>
                        <input type="hidden" id="audioFile" value="" required>
                        <small>选择一个音频文件或无视频流的文件</small>
                    </div>
                    <div class="param-group">
                        <label>时长处理模式</label>
                        <div class="duration-mode-info">
                            <div class="info-item">
                                <i class="fas fa-info-circle"></i>
                                <span><strong>音频时长 > 视频时长：</strong>循环重复视频适配音频时长</span>
                            </div>
                            <div class="info-item">
                                <i class="fas fa-info-circle"></i>
                                <span><strong>音频时长 < 视频时长：</strong>截取与音频等长的视频片段</span>
                            </div>
                        </div>
                    </div>
                `;
            case 'split':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>分割模式</label>
                        <select id="splitMode" onchange="videoProcessing.toggleSplitMode(this.value)">
                            <option value="fixed_length">按固定长度分割</option>
                            <option value="first_segment">指定第一段长度</option>
                        </select>
                    </div>
                    <div id="fixedLengthMode">
                        <div class="param-group">
                            <label>每段时长 (秒)</label>
                            <input type="number" id="segmentLength" value="60" min="1" max="3600" required>
                            <small>视频将按此时长分割成多个片段</small>
                        </div>
                    </div>
                    <div id="firstSegmentMode" style="display: none;">
                        <div class="param-group">
                            <label>第一段时长 (秒)</label>
                            <input type="number" id="firstSegmentLength" value="300" min="1" max="3600" required>
                            <small>指定第一段的时长，剩余部分作为第二段</small>
                        </div>
                    </div>
                    <div class="param-group">
                        <label>输出文件命名</label>
                        <div class="naming-preview">
                            <p><strong>命名规则：</strong></p>
                            <p>固定长度分割：<code>原文件名_part01.扩展名</code>、<code>原文件名_part02.扩展名</code>...</p>
                            <p>指定第一段：<code>原文件名_part01.扩展名</code>、<code>原文件名_part02.扩展名</code></p>
                        </div>
                    </div>
                `;
            case 'subtitle':
                return `
                    ${fileOptions}
                    <div class="param-group">
                        <label>字幕文件</label>
                        <div class="file-input-container">
                            <input type="file" id="subtitleFile" accept=".txt,.srt,.ass,.ssa" required>
                            <small>支持 .txt、.srt、.ass、.ssa 格式的字幕文件</small>
                        </div>
                    </div>
                    <div class="param-group">
                        <label>显示模式</label>
                        <select id="displayMode" onchange="videoProcessing.toggleSubtitleDisplayMode(this.value)">
                            <option value="proportional">按视频长度比例显示</option>
                            <option value="fixed">固定频率显示</option>
                        </select>
                        <small>比例显示：字幕行数平均分配到视频时长；固定频率：每行字幕显示固定时间</small>
                        
                        <!-- 显示当前选中模式的详细信息 -->
                        <div id="displayModeInfo" class="display-mode-info">
                            <div id="proportionalModeInfo" class="mode-info-item active">
                                <i class="fas fa-chart-line"></i>
                                <span>当前模式：按视频长度比例显示</span>
                                <small>字幕行数将根据视频总时长自动分配显示时间</small>
                            </div>
                            <div id="fixedModeInfo" class="mode-info-item" style="display: none;">
                                <i class="fas fa-clock"></i>
                                <span>当前模式：固定频率显示 - <strong id="currentFrequencyDisplay">2.0秒/行</strong></span>
                                <small>每行字幕将显示固定的时间长度</small>
                            </div>
                        </div>
                    </div>
                    <div id="fixedFrequencyMode" style="display: none;">
                        <div class="param-group">
                            <label>显示频率 (秒/行)</label>
                            <div class="frequency-input-group">
                                <input type="number" id="frequency" value="2" min="0.5" max="10" step="0.5" 
                                       onchange="videoProcessing.updateFrequencyDisplay(this.value)">
                                <div class="frequency-presets">
                                    <button type="button" class="preset-btn" onclick="videoProcessing.setFrequency(1)">1秒</button>
                                    <button type="button" class="preset-btn" onclick="videoProcessing.setFrequency(2)">2秒</button>
                                    <button type="button" class="preset-btn" onclick="videoProcessing.setFrequency(3)">3秒</button>
                                    <button type="button" class="preset-btn" onclick="videoProcessing.setFrequency(5)">5秒</button>
                                </div>
                            </div>
                            <small>每行字幕显示的时间长度，可使用预设值或自定义</small>
                        </div>
                    </div>
                    <div class="param-group">
                        <label>字体大小</label>
                        <input type="number" id="fontSize" value="24" min="12" max="72" step="2">
                        <small>字幕文字的大小，单位：像素</small>
                    </div>
                    <div class="param-group">
                        <label>字体颜色</label>
                        <select id="color">
                            <option value="white">白色</option>
                            <option value="black">黑色</option>
                            <option value="red">红色</option>
                            <option value="green">绿色</option>
                            <option value="blue">蓝色</option>
                            <option value="yellow">黄色</option>
                            <option value="cyan">青色</option>
                            <option value="magenta">洋红色</option>
                        </select>
                    </div>
                    <div class="param-group">
                        <label>字幕位置</label>
                        <select id="position">
                            <option value="bottom">底部</option>
                            <option value="top">顶部</option>
                            <option value="center">居中</option>
                        </select>
                    </div>
                    <div class="param-group">
                        <label class="checkbox-label">
                            <input type="checkbox" id="skipEmptyLines" checked>
                            <span class="checkmark"></span>
                            跳过空行
                        </label>
                        <small>勾选后将不显示字幕文件中的空行，否则显示空行</small>
                    </div>
                `;
            default:
                return '';
        }
    },
    
    // 生成文件选择选项
    generateFileOptions(operationType) {
        if (operationType === 'merge') {
            return ''; // 合并操作有特殊的文件选择界面
        }
        
        // 获取可用的视频文件
        const availableFiles = this.getAvailableVideoFiles();
        
        if (availableFiles.length === 0) {
            return `<div class="param-group">
                <div class="no-files-warning">
                    <i class="fas fa-exclamation-triangle"></i>
                    <p>暂无可处理的视频文件</p>
                    <p>请先下载一些视频文件</p>
                </div>
            </div>`;
        }
        
        // 检查是否为串行处理模式和是否为第一个操作
        const serialProcessing = document.getElementById('serialProcessing').checked;
        const isFirstOperation = this.workflow.length === 0;
        
        let fileSelectHTML = `<div class="param-group">
            <label>选择要处理的文件</label>`;
        
        if (serialProcessing && !isFirstOperation) {
            fileSelectHTML += `
            <div class="serial-mode-info">
                <i class="fas fa-info-circle"></i>
                <p>串行模式：此操作将自动使用前一步的输出文件</p>
                <p>您也可以手动选择其他文件覆盖此行为</p>
            </div>`;
        }
        
        // 使用新的文件选择器界面
        fileSelectHTML += this.generateFileSelector(availableFiles, serialProcessing, isFirstOperation);
        fileSelectHTML += `</div>`;
        return fileSelectHTML;
    },

    // 生成文件选择器界面
    generateFileSelector(availableFiles, serialProcessing = false, isFirstOperation = true) {
        const selectorId = `fileSelector_${Date.now()}`;
        
        let html = `
            <div class="file-selector" id="${selectorId}">
                <div class="file-selector-header">
                    <div class="file-view-modes">
                        <button type="button" class="view-mode-btn active" data-mode="search" title="搜索模式">
                            <i class="fas fa-search"></i>
                        </button>
                        <button type="button" class="view-mode-btn" data-mode="tree" title="目录树模式">
                            <i class="fas fa-folder-tree"></i>
                        </button>
                        <button type="button" class="view-mode-btn" data-mode="keyword" title="关键字分组">
                            <i class="fas fa-tags"></i>
                        </button>
                    </div>
                    <div class="file-search-container" id="searchContainer_${selectorId}">
                        <input type="text" class="file-search-input" placeholder="搜索文件..." 
                               onkeyup="videoProcessing.filterFiles('${selectorId}', this.value)">
                        <i class="fas fa-search"></i>
                    </div>
                    <div class="file-count">
                        共 <span class="total-files">${availableFiles.length}</span> 个文件
                    </div>
                </div>
                
                <div class="file-selector-body" id="fileSelectorBody_${selectorId}">
                    ${this.generateSearchModeContent(availableFiles, serialProcessing, isFirstOperation)}
                </div>
                
                <div class="file-selector-footer">
                    <div class="selected-file-info">
                        <span class="selected-text">未选择文件</span>
                    </div>
                </div>
                
                <input type="hidden" id="targetFile" value="" ${(!serialProcessing || isFirstOperation) ? 'required' : ''}>
            </div>`;
        
        // 添加事件监听器初始化脚本
        setTimeout(() => {
            this.initFileSelector(selectorId);
        }, 100);
        
        return html;
    },

    // 生成搜索模式内容
    generateSearchModeContent(availableFiles, serialProcessing, isFirstOperation) {
        let html = '';
        
        // 在串行模式下添加"使用前一步输出"选项
        if (serialProcessing && !isFirstOperation) {
            html += `
                <div class="file-option recommended" data-value="" data-filename="使用前一步输出">
                    <div class="file-option-content">
                        <div class="file-icon">
                            <i class="fas fa-arrow-right"></i>
                        </div>
                        <div class="file-info">
                            <div class="file-name">使用前一步输出（推荐）</div>
                            <div class="file-meta">自动使用工作流中前一步的输出文件</div>
                        </div>
                        <div class="file-select-indicator">
                            <i class="fas fa-check"></i>
                        </div>
                    </div>
                </div>`;
        }
        
        // 添加文件选项
        availableFiles.forEach((file, index) => {
            const fileInfo = this.getFileInfo(file);
            let metaInfo = [];
            
            // 添加基本信息
            metaInfo.push(fileInfo.size);
            metaInfo.push(fileInfo.date);
            
            // 如果有时长信息，添加时长显示
            if (fileInfo.duration) {
                metaInfo.push(`时长: ${fileInfo.duration}`);
            }
            
            // 如果有方向信息，添加方向显示
            if (fileInfo.orientation) {
                metaInfo.push(`方向: ${fileInfo.orientation}`);
            }
            
            // 如果有分辨率信息，添加分辨率显示
            if (fileInfo.resolution) {
                metaInfo.push(`分辨率: ${fileInfo.resolution}`);
            }
            
            // 如果有比特率信息，添加比特率显示
            if (fileInfo.bitrate) {
                metaInfo.push(`比特率: ${fileInfo.bitrate}`);
            }
            
            const metaInfoText = metaInfo.join(' • ');
            
            html += `
                <div class="file-option" data-value="${file}" data-filename="${file}">
                    <div class="file-option-content">
                        <div class="file-icon">
                            <i class="fas fa-video"></i>
                        </div>
                        <div class="file-info">
                            <div class="file-name">${file}</div>
                            <div class="file-meta">${metaInfoText}</div>
                        </div>
                        <div class="file-select-indicator">
                            <i class="fas fa-check"></i>
                        </div>
                    </div>
                </div>`;
        });
        
        return html;
    },

    // 生成树形目录模式内容
    generateTreeModeContent(availableFiles, serialProcessing, isFirstOperation) {
        let html = '';
        
        // 添加树形视图子模式选择器
        html += `
            <div class="tree-mode-selector">
                <div class="tree-mode-options">
                    <button type="button" class="tree-mode-btn active" data-tree-mode="smart" onclick="switchTreeMode(this, 'smart')">
                        <i class="fas fa-brain"></i>
                        <span>智能分组</span>
                    </button>
                    <button type="button" class="tree-mode-btn" data-tree-mode="directory" onclick="switchTreeMode(this, 'directory')">
                        <i class="fas fa-folder-tree"></i>
                        <span>目录结构</span>
                    </button>
                </div>
                <div class="tree-mode-description">
                    <span id="treeModeDesc">按文件名智能分组显示</span>
                </div>
            </div>
            <div id="treeContent">`;
        
        // 在串行模式下添加"使用前一步输出"选项
        if (serialProcessing && !isFirstOperation) {
            html += `
                <div class="file-option recommended" data-value="" data-filename="使用前一步输出">
                    <div class="file-option-content">
                        <div class="file-icon">
                            <i class="fas fa-arrow-right"></i>
                        </div>
                        <div class="file-info">
                            <div class="file-name">使用前一步输出（推荐）</div>
                            <div class="file-meta">自动使用工作流中前一步的输出文件</div>
                        </div>
                        <div class="file-select-indicator">
                            <i class="fas fa-check"></i>
                        </div>
                    </div>
                </div>`;
        }
        
        // 默认显示智能分组
        const fileTree = this.buildFileTree(availableFiles);
        html += this.renderFileTree(fileTree);
        html += '</div>';
        
        return html;
    },

    // 生成关键字分组模式内容
    async generateKeywordModeContent(availableFiles, serialProcessing, isFirstOperation) {
        let html = '';
        
        // 在串行模式下添加"使用前一步输出"选项
        if (serialProcessing && !isFirstOperation) {
            html += `
                <div class="file-option recommended" data-value="" data-filename="使用前一步输出">
                    <div class="file-option-content">
                        <div class="file-icon">
                            <i class="fas fa-arrow-right"></i>
                        </div>
                        <div class="file-info">
                            <div class="file-name">使用前一步输出（推荐）</div>
                            <div class="file-meta">自动使用工作流中前一步的输出文件</div>
                        </div>
                        <div class="file-select-indicator">
                            <i class="fas fa-check"></i>
                        </div>
                    </div>
                </div>`;
        }
        
        try {
            // 获取按搜索关键字分组的文件
            const response = await fetch('/api/downloads/by-search');
            const data = await response.json();
            
            if (data.filesBySearch && Object.keys(data.filesBySearch).length > 0) {
                // 渲染搜索关键字分组
                Object.keys(data.filesBySearch).forEach(originalKeyword => {
                    const searchGroup = data.filesBySearch[originalKeyword];
                    const files = searchGroup.files.filter(file => 
                        availableFiles.includes(file.title) || 
                        availableFiles.some(af => af.includes(file.title) || file.title.includes(af))
                    );
                    
                    if (files.length > 0) {
                        html += `
                            <div class="keyword-group">
                                <div class="keyword-group-header" onclick="this.parentElement.classList.toggle('collapsed')">
                                    <i class="fas fa-chevron-down group-toggle-icon"></i>
                                    <i class="fas fa-search keyword-icon"></i>
                                    <span class="keyword-name">搜索: ${originalKeyword}</span>
                                    <span class="keyword-count">(${files.length})</span>
                                </div>
                                <div class="keyword-group-content">
                                    ${files.map(file => {
                                        // 尝试匹配可用文件
                                        const matchedFile = availableFiles.find(af => 
                                            af.includes(file.title) || file.title.includes(af)
                                        ) || file.title;
                                        
                                        const fileInfo = this.getFileInfo(matchedFile);
                                        let metaInfo = `${fileInfo.size} • ${fileInfo.date}`;
                                        
                                        // 添加时长信息
                                        if (fileInfo.duration) {
                                            metaInfo += ` • 时长: ${fileInfo.duration}`;
                                        }
                                        
                                        // 添加方向信息
                                        if (fileInfo.orientation) {
                                            metaInfo += ` • 方向: ${fileInfo.orientation}`;
                                        }
                                        
                                        // 添加分辨率信息
                                        if (fileInfo.resolution) {
                                            metaInfo += ` • 分辨率: ${fileInfo.resolution}`;
                                        }
                                        
                                        // 添加比特率信息
                                        if (fileInfo.bitrate) {
                                            metaInfo += ` • 比特率: ${fileInfo.bitrate}`;
                                        }
                                        
                                        return `
                                            <div class="file-option" data-value="${matchedFile}" data-filename="${matchedFile}">
                                                <div class="file-option-content">
                                                    <div class="file-icon">
                                                        <i class="fas fa-video"></i>
                                                    </div>
                                                    <div class="file-info">
                                                        <div class="file-name">${matchedFile}</div>
                                                        <div class="file-meta">${metaInfo}</div>
                                                    </div>
                                                    <div class="file-select-indicator">
                                                        <i class="fas fa-check"></i>
                                                    </div>
                                                </div>
                                            </div>`;
                                    }).join('')}
                                </div>
                            </div>`;
                    }
                });
            }
            
            // 如果没有搜索关键字分组，或者有未分组的文件，添加"其他文件"分组
            const groupedFiles = new Set();
            if (data.filesBySearch) {
                Object.values(data.filesBySearch).forEach(group => {
                    group.files.forEach(file => {
                        const matchedFile = availableFiles.find(af => 
                            af.includes(file.title) || file.title.includes(af)
                        );
                        if (matchedFile) {
                            groupedFiles.add(matchedFile);
                        }
                    });
                });
            }
            
            const ungroupedFiles = availableFiles.filter(file => !groupedFiles.has(file));
            
            if (ungroupedFiles.length > 0) {
                html += `
                    <div class="keyword-group">
                        <div class="keyword-group-header" onclick="this.parentElement.classList.toggle('collapsed')">
                            <i class="fas fa-chevron-down group-toggle-icon"></i>
                            <i class="fas fa-folder keyword-icon"></i>
                            <span class="keyword-name">其他文件</span>
                            <span class="keyword-count">(${ungroupedFiles.length})</span>
                        </div>
                        <div class="keyword-group-content">
                            ${ungroupedFiles.map(file => {
                                const fileInfo = this.getFileInfo(file);
                                let metaInfo = `${fileInfo.size} • ${fileInfo.date}`;
                                
                                // 添加时长信息
                                if (fileInfo.duration) {
                                    metaInfo += ` • 时长: ${fileInfo.duration}`;
                                }
                                
                                // 添加方向信息
                                if (fileInfo.orientation) {
                                    metaInfo += ` • 方向: ${fileInfo.orientation}`;
                                }
                                
                                // 添加分辨率信息
                                if (fileInfo.resolution) {
                                    metaInfo += ` • 分辨率: ${fileInfo.resolution}`;
                                }
                                
                                // 添加比特率信息
                                if (fileInfo.bitrate) {
                                    metaInfo += ` • 比特率: ${fileInfo.bitrate}`;
                                }
                                
                                return `
                                    <div class="file-option" data-value="${file}" data-filename="${file}">
                                        <div class="file-option-content">
                                            <div class="file-icon">
                                                <i class="fas fa-video"></i>
                                            </div>
                                            <div class="file-info">
                                                <div class="file-name">${file}</div>
                                                <div class="file-meta">${metaInfo}</div>
                                            </div>
                                            <div class="file-select-indicator">
                                                <i class="fas fa-check"></i>
                                            </div>
                                        </div>
                                    </div>`;
                            }).join('')}
                        </div>
                    </div>`;
            }
            
        } catch (error) {
            console.error('获取搜索关键字分组失败:', error);
            // 如果API调用失败，回退到原有的关键字分组逻辑
            const keywordGroups = this.groupFilesByKeywords(availableFiles);
            Object.keys(keywordGroups).forEach(keyword => {
                const files = keywordGroups[keyword];
                html += `
                    <div class="keyword-group">
                        <div class="keyword-group-header" onclick="this.parentElement.classList.toggle('collapsed')">
                            <i class="fas fa-chevron-down group-toggle-icon"></i>
                            <i class="fas fa-tag keyword-icon"></i>
                            <span class="keyword-name">${keyword}</span>
                            <span class="keyword-count">(${files.length})</span>
                        </div>
                        <div class="keyword-group-content">
                            ${files.map(file => {
                                const fileInfo = this.getFileInfo(file);
                                let metaInfo = [];
                                
                                // 构建元数据信息
                                if (fileInfo.size) metaInfo.push(fileInfo.size);
                                if (fileInfo.date) metaInfo.push(fileInfo.date);
                                if (fileInfo.duration) metaInfo.push(fileInfo.duration);
                                if (fileInfo.orientation) metaInfo.push(fileInfo.orientation);
                                if (fileInfo.resolution) metaInfo.push(fileInfo.resolution);
                                if (fileInfo.bitrate) metaInfo.push(fileInfo.bitrate);
                                
                                return `
                                    <div class="file-option" data-value="${file}" data-filename="${file}">
                                        <div class="file-option-content">
                                            <div class="file-icon">
                                                <i class="fas fa-video"></i>
                                            </div>
                                            <div class="file-info">
                                                <div class="file-name">${file}</div>
                                                <div class="file-meta">${metaInfo.join(' • ')}</div>
                                            </div>
                                            <div class="file-select-indicator">
                                                <i class="fas fa-check"></i>
                                            </div>
                                        </div>
                                    </div>`;
                            }).join('')}
                        </div>
                    </div>`;
            });
        }
        
        return html;
    },

    // 构建文件树结构
    buildFileTree(files) {
        const tree = {};
        
        files.forEach(file => {
            // 解析文件路径，提取可能的目录结构
            const parts = this.parseFilePath(file);
            let current = tree;
            
            // 构建目录层级
            for (let i = 0; i < parts.length - 1; i++) {
                const part = parts[i];
                if (!current[part]) {
                    current[part] = { _files: [], _dirs: {} };
                }
                current = current[part]._dirs;
            }
            
            // 添加文件到最终目录
            const fileName = parts[parts.length - 1];
            const parentDir = parts.length > 1 ? parts[parts.length - 2] : 'root';
            
            if (!current[parentDir]) {
                current[parentDir] = { _files: [], _dirs: {} };
            }
            current[parentDir]._files.push(file);
        });
        
        return tree;
    },

    // 解析文件路径
    parseFilePath(fileName) {
        // 尝试从文件名中提取目录信息
        // 例如：从 "WHAT IF Naruto Is Real [1of2] - AI Brought him to Life ｜ Abhishek Anand.mp4"
        // 提取可能的分组信息
        
        const parts = [];
        
        // 检查是否包含系列标识
        const seriesMatch = fileName.match(/\[(\d+)of(\d+)\]|\[Part\s*(\d+)\]|\[第(\d+)集\]/i);
        if (seriesMatch) {
            const seriesName = fileName.split(/\[/)[0].trim();
            parts.push(seriesName);
        }
        
        // 检查是否包含作者信息
        const authorMatch = fileName.match(/[｜|]\s*([^.]+)\.mp4$/);
        if (authorMatch) {
            parts.push('作者: ' + authorMatch[1].trim());
        }
        
        // 如果没有找到明显的分组，按首字母分组
        if (parts.length === 0) {
            const firstChar = fileName.charAt(0).toUpperCase();
            if (/[A-Z]/.test(firstChar)) {
                parts.push(firstChar);
            } else if (/[\u4e00-\u9fa5]/.test(firstChar)) {
                parts.push('中文');
            } else if (/[0-9]/.test(firstChar)) {
                parts.push('数字');
            } else {
                parts.push('其他');
            }
        }
        
        parts.push(fileName);
        return parts;
    },

    // 渲染文件树
    renderFileTree(tree, level = 0) {
        let html = '';
        
        Object.keys(tree).forEach(key => {
            const node = tree[key];
            if (node._files && node._files.length > 0) {
                html += `
                    <div class="tree-node" style="margin-left: ${level * 20}px">
                        <div class="tree-folder" onclick="this.parentElement.classList.toggle('collapsed')">
                            <i class="fas fa-chevron-down tree-toggle-icon"></i>
                            <i class="fas fa-folder folder-icon"></i>
                            <span class="folder-name">${key}</span>
                            <span class="folder-count">(${node._files.length})</span>
                        </div>
                        <div class="tree-content">
                            ${node._files.map(file => {
                                const fileInfo = this.getFileInfo(file);
                                let metaInfo = `${fileInfo.size} • ${fileInfo.date}`;
                                
                                // 添加时长信息
                                if (fileInfo.duration) {
                                    metaInfo += ` • 时长: ${fileInfo.duration}`;
                                }
                                
                                // 添加方向信息
                                if (fileInfo.orientation) {
                                    metaInfo += ` • 方向: ${fileInfo.orientation}`;
                                }
                                
                                // 添加分辨率信息
                                if (fileInfo.resolution) {
                                    metaInfo += ` • 分辨率: ${fileInfo.resolution}`;
                                }
                                
                                // 添加比特率信息
                                if (fileInfo.bitrate) {
                                    metaInfo += ` • 比特率: ${fileInfo.bitrate}`;
                                }
                                
                                return `
                                    <div class="file-option tree-file" data-value="${file}" data-filename="${file}" style="margin-left: 20px">
                                        <div class="file-option-content">
                                            <div class="file-icon">
                                                <i class="fas fa-video"></i>
                                            </div>
                                            <div class="file-info">
                                                <div class="file-name">${file}</div>
                                                <div class="file-meta">${metaInfo}</div>
                                            </div>
                                            <div class="file-select-indicator">
                                                <i class="fas fa-check"></i>
                                            </div>
                                        </div>
                                    </div>`;
                            }).join('')}
                        </div>
                    </div>`;
            }
            
            if (node._dirs && Object.keys(node._dirs).length > 0) {
                html += this.renderFileTree(node._dirs, level + 1);
            }
        });
        
        return html;
    },

    // 渲染实际目录结构
    renderDirectoryStructure(directoryData, level = 0) {
        if (!directoryData) return '';
        
        let html = '';
        
        if (directoryData.type === 'folder') {
            const hasChildren = directoryData.children && directoryData.children.length > 0;
            
            // 调试信息
            console.log(`渲染目录: ${directoryData.name}, 层级: ${level}, 子项数量: ${directoryData.children ? directoryData.children.length : 0}`);
            
            // 生成唯一的ID用于事件处理
            const nodeId = `tree-node-${Math.random().toString(36).substr(2, 9)}`;
            
            html += `
                <div class="tree-node directory-node" id="${nodeId}" style="margin-left: ${level * 20}px">
                    <div class="tree-folder directory-folder" onclick="videoProcessing.toggleDirectoryNode('${nodeId}')">
                        <i class="fas fa-chevron-down tree-toggle-icon"></i>
                        <i class="fas fa-folder folder-icon"></i>
                        <span class="folder-name">${directoryData.name}</span>
                        <span class="folder-stats">(${directoryData.totalFiles || 0} 文件, ${this.formatFileSize(directoryData.totalSize || 0)})</span>
                    </div>
                    <div class="tree-content directory-content">`;
            
            if (hasChildren) {
                // 分离文件夹和文件
                const folders = directoryData.children.filter(child => child.type === 'folder');
                const files = directoryData.children.filter(child => child.type === 'file');
                
                console.log(`目录 ${directoryData.name} 包含: ${folders.length} 个子目录, ${files.length} 个文件`);
                
                // 先渲染子文件夹
                folders.forEach(folder => {
                    html += this.renderDirectoryStructure(folder, level + 1);
                });
                
                // 再渲染文件
                files.forEach(file => {
                    console.log(`处理文件: ${file.name}, isVideo: ${file.isVideo}, isAudio: ${file.isAudio}, type: ${file.type}`);
                    
                    // 显示所有文件（视频和音频）
                    let durationInfo = '';
                    if (file.duration) {
                        durationInfo = ` • ${this.formatDuration(file.duration)}`;
                    }
                    
                    // 添加视频详细信息
                    let videoInfo = '';
                    if (file.isVideo && (file.bitrate || file.orientation || file.width || file.height)) {
                        const bitrateText = file.bitrate ? `${formatBitrate(file.bitrate)}` : '';
                        const orientationText = file.orientation ? `${formatOrientation(file.orientation)}` : '';
                        const resolutionText = (file.width && file.height) ? `${formatResolution(file.width, file.height)}` : '';
                        
                        const videoDetails = [bitrateText, orientationText, resolutionText].filter(Boolean).join(' • ');
                        if (videoDetails) {
                            videoInfo = ` • ${videoDetails}`;
                        }
                    }
                    
                    // 根据文件类型确定图标
                    let iconClass = 'fas fa-file';
                    if (file.isVideo) {
                        iconClass = 'fas fa-video';
                    } else if (file.isAudio) {
                        iconClass = 'fas fa-music';
                    } else {
                        // 根据文件扩展名确定图标
                        const fileExt = file.name.split('.').pop().toLowerCase();
                        const isVideoFile = ['mp4', 'mkv', 'avi', 'mov', 'webm', 'flv', 'm4v'].includes(fileExt);
                        const isAudioFile = ['mp3', 'wav', 'flac', 'aac', 'm4a', 'ogg'].includes(fileExt);
                        if (isVideoFile) {
                            iconClass = 'fas fa-video';
                        } else if (isAudioFile) {
                            iconClass = 'fas fa-music';
                        }
                    }
                    
                    html += `
                        <div class="file-option tree-file directory-file" data-value="${file.name}" data-filename="${file.name}" data-filepath="${file.path}" style="margin-left: ${(level + 1) * 20}px">
                            <div class="file-option-content">
                                <div class="file-icon">
                                    <i class="${iconClass}"></i>
                                </div>
                                <div class="file-info">
                                    <div class="file-name">${file.name}</div>
                                    <div class="file-meta">${this.formatFileSize(file.size)} • ${this.formatDateTime(file.created)}${durationInfo}${videoInfo}</div>
                                </div>
                                <div class="file-select-indicator">
                                    <i class="fas fa-check"></i>
                                </div>
                            </div>
                        </div>`;
                });
            } else {
                // 如果没有子项，显示空目录提示
                html += `
                    <div class="empty-folder-notice" style="margin-left: ${(level + 1) * 20}px; padding: 8px; color: #999; font-style: italic;">
                        <i class="fas fa-folder-open"></i> 空目录
                    </div>`;
            }
            
            html += `
                    </div>
                </div>`;
        }
        
        return html;
    },

    // 切换目录节点的展开/折叠状态
    toggleDirectoryNode(nodeId) {
        const node = document.getElementById(nodeId);
        if (node) {
            node.classList.toggle('collapsed');
        }
    },

    // 格式化文件大小
    formatFileSize(bytes) {
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },

    // 格式化时长
    formatDuration(seconds) {
        if (!seconds || isNaN(seconds)) return '';
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        
        if (hours > 0) {
            return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        } else {
            return `${minutes}:${secs.toString().padStart(2, '0')}`;
        }
    },

    // 格式化日期时间
    formatDateTime(dateString) {
        const date = new Date(dateString);
        return date.toLocaleString('zh-CN', {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit'
        });
    },

    // 按关键字分组文件
    groupFilesByKeywords(files) {
        const groups = {};
        
        files.forEach(file => {
            const keywords = this.extractKeywords(file);
            
            keywords.forEach(keyword => {
                if (!groups[keyword]) {
                    groups[keyword] = [];
                }
                groups[keyword].push(file);
            });
        });
        
        // 如果某个文件没有关键字，放入"其他"分组
        files.forEach(file => {
            const keywords = this.extractKeywords(file);
            if (keywords.length === 0) {
                if (!groups['其他']) {
                    groups['其他'] = [];
                }
                groups['其他'].push(file);
            }
        });
        
        return groups;
    },

    // 提取文件关键字
    extractKeywords(fileName) {
        const keywords = [];
        
        // 提取系列信息
        const seriesMatch = fileName.match(/\[(\d+)of(\d+)\]|\[Part\s*(\d+)\]|\[第(\d+)集\]/i);
        if (seriesMatch) {
            keywords.push('系列视频');
        }
        
        // 提取语言信息
        if (/[\u4e00-\u9fa5]/.test(fileName)) {
            keywords.push('中文');
        }
        if (/[a-zA-Z]/.test(fileName)) {
            keywords.push('英文');
        }
        
        // 提取常见关键字
        const commonKeywords = [
            'tutorial', '教程', 'guide', '指南',
            'review', '评测', '测评',
            'gameplay', '游戏', 'game',
            'music', '音乐', 'song',
            'movie', '电影', 'film',
            'anime', '动漫', '动画',
            'news', '新闻', '资讯',
            'vlog', '日常', 'daily',
            'cooking', '烹饪', '美食',
            'travel', '旅行', '旅游'
        ];
        
        commonKeywords.forEach(keyword => {
            if (fileName.toLowerCase().includes(keyword.toLowerCase())) {
                keywords.push(keyword.includes('\\u') ? keyword : keyword.charAt(0).toUpperCase() + keyword.slice(1));
            }
        });
        
        // 提取文件格式
        const ext = fileName.split('.').pop().toLowerCase();
        if (ext) {
            keywords.push(ext.toUpperCase());
        }
        
        return [...new Set(keywords)]; // 去重
    },

    // 初始化文件选择器
    initFileSelector(selectorId) {
        const selector = document.getElementById(selectorId);
        if (!selector) return;

        // 初始化模式切换按钮
        this.initViewModeButtons(selectorId);
        
        // 初始化文件选项点击事件
        this.initFileOptionEvents(selectorId);
        
        // 如果是推荐选项，默认选中
        const recommendedOption = selector.querySelector('.file-option.recommended');
        if (recommendedOption) {
            recommendedOption.click();
        }
    },

    // 初始化视图模式按钮
    initViewModeButtons(selectorId) {
        const selector = document.getElementById(selectorId);
        const modeButtons = selector.querySelectorAll('.view-mode-btn');
        const selectorBody = selector.querySelector(`#fileSelectorBody_${selectorId}`);
        const searchContainer = selector.querySelector(`#searchContainer_${selectorId}`);
        
        modeButtons.forEach(button => {
            button.addEventListener('click', () => {
                // 移除其他按钮的激活状态
                modeButtons.forEach(btn => btn.classList.remove('active'));
                
                // 激活当前按钮
                button.classList.add('active');
                
                const mode = button.dataset.mode;
                const availableFiles = this.getAvailableVideoFiles();
                const serialProcessing = document.getElementById('serialProcessing')?.checked || false;
                const isFirstOperation = this.workflow.length === 0;
                
                // 根据模式切换内容
                let content = '';
                switch (mode) {
                    case 'search':
                        content = this.generateSearchModeContent(availableFiles, serialProcessing, isFirstOperation);
                        searchContainer.style.display = 'block';
                        selectorBody.innerHTML = content;
                        break;
                    case 'tree':
                        content = this.generateTreeModeContent(availableFiles, serialProcessing, isFirstOperation);
                        searchContainer.style.display = 'none';
                        selectorBody.innerHTML = content;
                        break;
                    case 'keyword':
                        searchContainer.style.display = 'none';
                        // 显示加载状态
                        selectorBody.innerHTML = '<div class="loading-state"><i class="fas fa-spinner fa-spin"></i> 正在加载关键字分组...</div>';
                        // 异步加载关键字分组内容
                        this.generateKeywordModeContent(availableFiles, serialProcessing, isFirstOperation)
                            .then(content => {
                                selectorBody.innerHTML = content;
                                // 重新初始化文件选项事件
                                // 检查是否是合并文件选择器模态框
                                const modal = selector.closest('#mergeFileSelectModal');
                                if (modal) {
                                    // 如果是合并文件选择器，使用合并模式的事件初始化
                                    mergeFileManager.initMergeFileSelector(selectorId);
                                } else {
                                    // 否则使用普通的文件选项事件初始化
                                    this.initFileOptionEvents(selectorId);
                                    // 如果是推荐选项，默认选中
                                    const recommendedOption = selector.querySelector('.file-option.recommended');
                                    if (recommendedOption) {
                                        recommendedOption.click();
                                    }
                                }
                            })
                            .catch(error => {
                                console.error('加载关键字分组失败:', error);
                                selectorBody.innerHTML = '<div class="error-state"><i class="fas fa-exclamation-triangle"></i> 加载关键字分组失败</div>';
                            });
                        return; // 提前返回，避免执行后续代码
                }
                
                // 对于非关键字模式，直接设置内容（已在switch中处理）
                
                // 重新初始化文件选项事件
                // 检查是否是合并文件选择器模态框
                const modal = selector.closest('#mergeFileSelectModal');
                if (modal) {
                    // 如果是合并文件选择器，使用合并模式的事件初始化
                    mergeFileManager.initMergeFileSelector(selectorId);
                } else {
                    // 否则使用普通的文件选项事件初始化
                    this.initFileOptionEvents(selectorId);
                    
                    // 如果是推荐选项，默认选中
                    const recommendedOption = selector.querySelector('.file-option.recommended');
                    if (recommendedOption) {
                        recommendedOption.click();
                    }
                }
            });
        });
    },

    // 初始化文件选项事件
    initFileOptionEvents(selectorIdOrContainer) {
        let container;
        
        // 如果传入的是字符串ID，则查找对应元素；如果是DOM元素，直接使用
        if (typeof selectorIdOrContainer === 'string') {
            container = document.getElementById(selectorIdOrContainer);
        } else {
            container = selectorIdOrContainer;
        }
        
        if (!container) return;
        
        // 在容器中查找文件选择器
        const selector = container.querySelector('.file-selector') || container;
        const fileOptions = selector.querySelectorAll('.file-option');
        const hiddenInput = selector.querySelector('#targetFile');
        const selectedInfo = selector.querySelector('.selected-text');
        
        fileOptions.forEach(option => {
            option.addEventListener('click', () => {
                // 移除其他选项的选中状态
                fileOptions.forEach(opt => opt.classList.remove('selected'));
                
                // 选中当前选项
                option.classList.add('selected');
                
                // 更新隐藏输入框的值
                const value = option.dataset.value;
                const filename = option.dataset.filename;
                hiddenInput.value = value;
                
                // 更新选中文件信息显示
                selectedInfo.textContent = filename;
                
                // 如果在模态框中，更新预览按钮状态
                const modal = container.closest('.operation-modal');
                if (modal) {
                    this.updatePreviewButtonState(modal);
                } else {
                    // 原有的逻辑，用于非模态框情况
                    const currentOperation = document.querySelector('.operation-card.active')?.dataset.operation;
                    if (currentOperation === 'mosaic' && filename && this.isVideoFile(filename)) {
                        const previewBtn = document.getElementById('previewVideoBtn');
                        if (previewBtn) {
                            previewBtn.disabled = false;
                            previewBtn.innerHTML = '<i class="fas fa-eye"></i> 预览视频';
                        }
                    } else {
                        const previewBtn = document.getElementById('previewVideoBtn');
                        if (previewBtn) {
                            previewBtn.disabled = true;
                            previewBtn.innerHTML = '<i class="fas fa-eye"></i> 预览视频';
                        }
                    }
                }
                
                // 触发change事件
                hiddenInput.dispatchEvent(new Event('change'));
            });
        });
    },

    // 过滤文件
    filterFiles(selectorId, searchTerm) {
        const selector = document.getElementById(selectorId);
        if (!selector) return;
        
        const fileOptions = selector.querySelectorAll('.file-option');
        const totalFilesSpan = selector.querySelector('.total-files');
        let visibleCount = 0;
        
        fileOptions.forEach(option => {
            const filename = option.dataset.filename.toLowerCase();
            const isVisible = filename.includes(searchTerm.toLowerCase());
            
            option.style.display = isVisible ? 'block' : 'none';
            if (isVisible) visibleCount++;
        });
        
        // 更新文件计数
        if (searchTerm) {
            totalFilesSpan.textContent = `${visibleCount}/${fileOptions.length}`;
        } else {
            totalFilesSpan.textContent = fileOptions.length;
        }
    },

    // 获取文件信息
    getFileInfo(fileName) {
        // 从下载列表中获取文件信息
        const downloadsList = document.getElementById('downloadsList');
        const fileElements = downloadsList.querySelectorAll('.download-item');
        
        for (let element of fileElements) {
            const nameElement = element.querySelector('.file-name');
            if (nameElement && nameElement.textContent === fileName) {
                const sizeElement = element.querySelector('.file-size');
                const dateElement = element.querySelector('.file-date');
                const durationElement = element.querySelector('.file-duration');
                const orientationElement = element.querySelector('.file-orientation');
                const resolutionElement = element.querySelector('.file-resolution');
                const bitrateElement = element.querySelector('.file-bitrate');
                
                let duration = null;
                if (durationElement) {
                    // 提取时长文本，去掉前缀符号
                    const durationText = durationElement.textContent.replace('⏱️ ', '').trim();
                    duration = durationText;
                }
                
                let orientation = null;
                if (orientationElement) {
                    const orientationText = orientationElement.textContent.replace('📱 ', '').trim();
                    orientation = orientationText;
                }
                
                let resolution = null;
                if (resolutionElement) {
                    const resolutionText = resolutionElement.textContent.replace('📐 ', '').trim();
                    resolution = resolutionText;
                }
                
                let bitrate = null;
                if (bitrateElement) {
                    const bitrateText = bitrateElement.textContent.replace('⚡ ', '').trim();
                    bitrate = bitrateText;
                }
                
                return {
                    size: sizeElement ? sizeElement.textContent.replace('📊 ', '') : '未知大小',
                    date: dateElement ? dateElement.textContent.replace('🕒 ', '') : '未知日期',
                    duration: duration,
                    orientation: orientation,
                    resolution: resolution,
                    bitrate: bitrate
                };
            }
        }
        
        return {
            size: '未知大小',
            date: '未知日期',
            duration: null,
            orientation: null,
            resolution: null,
            bitrate: null
        };
    },

    // 获取可用的视频文件
    getAvailableVideoFiles() {
        // 从下载列表中获取视频文件
        const downloadsList = document.getElementById('downloadsList');
        const fileElements = downloadsList.querySelectorAll('.download-item');
        const videoFiles = [];
        
        fileElements.forEach(element => {
            const fileName = element.querySelector('.file-name')?.textContent;
            if (fileName && this.isVideoFile(fileName)) {
                videoFiles.push(fileName);
            }
        });
        
        return videoFiles;
    },

    // 生成视频文件选项
    generateVideoFileOptions() {
        const videoFiles = this.getAvailableVideoFiles();
        
        if (videoFiles.length === 0) {
            return `<div class="no-files-warning">
                <i class="fas fa-exclamation-triangle"></i>
                <p>暂无可用的视频文件</p>
                <p>请先下载一些视频文件</p>
            </div>`;
        }
        
        let html = '';
        videoFiles.forEach((file, index) => {
            const fileInfo = this.getFileInfo(file);
            html += `
                <div class="file-option" data-value="${file}" data-filename="${file}" 
                     onclick="videoProcessing.selectBackgroundVideoFile('${file}', this)">
                    <div class="file-option-content">
                        <div class="file-icon">
                            <i class="fas fa-video"></i>
                        </div>
                        <div class="file-info">
                            <div class="file-name">${file}</div>
                            <div class="file-meta">${fileInfo.size || '未知大小'} • ${fileInfo.date || '未知日期'} • ${fileInfo.duration || '未知时长'}${fileInfo.orientation ? ' • ' + fileInfo.orientation : ''}${fileInfo.resolution ? ' • ' + fileInfo.resolution : ''}${fileInfo.bitrate ? ' • ' + fileInfo.bitrate : ''}</div>
                        </div>
                        <div class="file-select-indicator">
                            <i class="fas fa-check"></i>
                        </div>
                    </div>
                </div>`;
        });
        
        return html;
    },

    // 生成音频文件选项
    generateAudioFileOptions() {
        const downloadsList = document.getElementById('downloadsList');
        const fileElements = downloadsList.querySelectorAll('.download-item');
        const audioFiles = [];
        
        fileElements.forEach(element => {
            const fileName = element.querySelector('.file-name')?.textContent;
            if (fileName && this.isAudioFile(fileName)) {
                audioFiles.push(fileName);
            }
        });
        
        if (audioFiles.length === 0) {
            return `<div class="no-files-warning">
                <i class="fas fa-exclamation-triangle"></i>
                <p>暂无可用的音频文件</p>
                <p>请先下载一些音频文件或无视频流的文件</p>
            </div>`;
        }
        
        let html = '';
        audioFiles.forEach((file, index) => {
            const fileInfo = this.getFileInfo(file);
            html += `
                <div class="file-option" data-value="${file}" data-filename="${file}" 
                     onclick="videoProcessing.selectAudioFile('${file}', this)">
                    <div class="file-option-content">
                        <div class="file-icon">
                            <i class="fas fa-music"></i>
                        </div>
                        <div class="file-info">
                            <div class="file-name">${file}</div>
                            <div class="file-meta">${fileInfo.size || '未知大小'} • ${fileInfo.date || '未知日期'} • ${fileInfo.duration || '未知时长'}${fileInfo.orientation ? ' • ' + fileInfo.orientation : ''}${fileInfo.resolution ? ' • ' + fileInfo.resolution : ''}${fileInfo.bitrate ? ' • ' + fileInfo.bitrate : ''}</div>
                        </div>
                        <div class="file-select-indicator">
                            <i class="fas fa-check"></i>
                        </div>
                    </div>
                </div>`;
        });
        
        return html;
    },

    // 判断是否为视频文件
    isVideoFile(fileName) {
        const videoExtensions = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.m4v'];
        const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
        return videoExtensions.includes(extension);
    },

    // 判断是否为音频文件
    isAudioFile(fileName) {
        const audioExtensions = ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.m4a', '.wma'];
        const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
        return audioExtensions.includes(extension);
    },

    // 获取选中的合并文件
    getSelectedMergeFiles() {
        return mergeFileManager.getSelectedFiles();
    },

    // 切换缩放模式
    toggleScaleMode(mode) {
        const dimensionsMode = document.getElementById('dimensionsMode');
        const ratioMode = document.getElementById('ratioMode');
        
        if (mode === 'dimensions') {
            dimensionsMode.style.display = 'block';
            ratioMode.style.display = 'none';
        } else {
            dimensionsMode.style.display = 'none';
            ratioMode.style.display = 'block';
        }
    },

    // 切换水印类型
    toggleWatermarkType(type) {
        const textWatermark = document.getElementById('textWatermark');
        const imageWatermark = document.getElementById('imageWatermark');
        
        if (type === 'text') {
            textWatermark.style.display = 'block';
            imageWatermark.style.display = 'none';
        } else {
            textWatermark.style.display = 'none';
            imageWatermark.style.display = 'block';
        }
    },

    // 切换分割模式
    toggleSplitMode(mode) {
        const fixedLengthMode = document.getElementById('fixedLengthMode');
        const firstSegmentMode = document.getElementById('firstSegmentMode');
        
        if (mode === 'fixed_length') {
            fixedLengthMode.style.display = 'block';
            firstSegmentMode.style.display = 'none';
        } else {
            fixedLengthMode.style.display = 'none';
            firstSegmentMode.style.display = 'block';
        }
    },

    // 切换字幕显示模式
    toggleSubtitleDisplayMode(mode) {
        const proportionalMode = document.getElementById('proportionalMode');
        const fixedFrequencyMode = document.getElementById('fixedFrequencyMode');
        const proportionalModeInfo = document.getElementById('proportionalModeInfo');
        const fixedModeInfo = document.getElementById('fixedModeInfo');
        
        if (mode === 'proportional') {
            if (proportionalMode) proportionalMode.style.display = 'block';
            if (fixedFrequencyMode) fixedFrequencyMode.style.display = 'none';
            if (proportionalModeInfo) {
                proportionalModeInfo.style.display = 'block';
                proportionalModeInfo.classList.add('active');
            }
            if (fixedModeInfo) {
                fixedModeInfo.style.display = 'none';
                fixedModeInfo.classList.remove('active');
            }
        } else if (mode === 'fixed') {
            if (proportionalMode) proportionalMode.style.display = 'none';
            if (fixedFrequencyMode) fixedFrequencyMode.style.display = 'block';
            if (proportionalModeInfo) {
                proportionalModeInfo.style.display = 'none';
                proportionalModeInfo.classList.remove('active');
            }
            if (fixedModeInfo) {
                fixedModeInfo.style.display = 'block';
                fixedModeInfo.classList.add('active');
            }
            // 更新频率显示
            const frequencyInput = document.getElementById('frequency');
            if (frequencyInput) {
                this.updateFrequencyDisplay(frequencyInput.value);
            }
        }
    },

    // 更新频率显示
    updateFrequencyDisplay(frequency) {
        const currentFrequencyDisplay = document.getElementById('currentFrequencyDisplay');
        if (currentFrequencyDisplay) {
            currentFrequencyDisplay.textContent = `${parseFloat(frequency).toFixed(1)}秒/行`;
        }
    },

    // 设置预设频率
    setFrequency(frequency) {
        const frequencyInput = document.getElementById('frequency');
        if (frequencyInput) {
            frequencyInput.value = frequency;
            this.updateFrequencyDisplay(frequency);
            
            // 添加视觉反馈
            const presetBtns = document.querySelectorAll('.preset-btn');
            presetBtns.forEach(btn => btn.classList.remove('active'));
            event.target.classList.add('active');
            
            // 短暂高亮效果
            setTimeout(() => {
                event.target.classList.remove('active');
            }, 200);
        }
    },

    // 选择背景视频文件
    selectBackgroundVideoFile(filename, element) {
        // 移除其他选中状态
        const container = element.closest('.file-selector-body');
        container.querySelectorAll('.file-option').forEach(option => {
            option.classList.remove('selected');
        });
        
        // 添加选中状态
        element.classList.add('selected');
        
        // 更新隐藏字段
        const hiddenInput = document.getElementById('backgroundVideoFile');
        if (hiddenInput) {
            hiddenInput.value = filename;
        }
        
        // 更新显示文本
        const selectedInfo = container.closest('.file-selector').querySelector('.selected-text');
        if (selectedInfo) {
            selectedInfo.textContent = filename;
        }
    },

    // 选择音频文件
    selectAudioFile(filename, element) {
        // 移除其他选中状态
        const container = element.closest('.file-selector-body');
        container.querySelectorAll('.file-option').forEach(option => {
            option.classList.remove('selected');
        });
        
        // 添加选中状态
        element.classList.add('selected');
        
        // 更新隐藏字段
        const hiddenInput = document.getElementById('audioFile');
        if (hiddenInput) {
            hiddenInput.value = filename;
        }
        
        // 更新显示文本
        const selectedInfo = container.closest('.file-selector').querySelector('.selected-text');
        if (selectedInfo) {
            selectedInfo.textContent = filename;
        }
    },

    // 过滤背景视频文件
    filterBackgroundVideoFiles(searchTerm) {
        const container = document.getElementById('backgroundVideoList');
        if (!container) return;
        
        const fileOptions = container.querySelectorAll('.file-option');
        fileOptions.forEach(option => {
            const filename = option.dataset.filename || '';
            const isVisible = filename.toLowerCase().includes(searchTerm.toLowerCase());
            option.style.display = isVisible ? 'block' : 'none';
        });
    },

    // 过滤音频文件
    filterAudioFiles(searchTerm) {
        const container = document.getElementById('audioFileList');
        if (!container) return;
        
        const fileOptions = container.querySelectorAll('.file-option');
        fileOptions.forEach(option => {
            const filename = option.dataset.filename || '';
            const isVisible = filename.toLowerCase().includes(searchTerm.toLowerCase());
            option.style.display = isVisible ? 'block' : 'none';
        });
    },

    // 添加操作到工作流
    addOperation(operationType, modal) {
        const params = this.getOperationParams(operationType, modal);
        if (!params) return;
        
        const operation = {
            type: operationType,
            params: params,
            id: Date.now()
        };
        
        this.workflow.push(operation);
        this.updateWorkflowDisplay();
        this.updateButtonStates();
        modal.remove();
    },
    
    // 获取操作参数
    getOperationParams(operationType, modal) {
        const params = {};
        
        try {
            // 获取目标文件（除了合并操作）
            if (operationType !== 'merge') {
                const targetFileElement = modal.querySelector('#targetFile');
                if (targetFileElement) {
                    params.targetFile = targetFileElement.value;
                    
                    // 检查是否为串行处理模式
                    const serialProcessing = document.getElementById('serialProcessing').checked;
                    const isFirstOperation = this.workflow.length === 0;
                    
                    // 在串行模式下，只有第一个操作需要选择文件
                    if (!params.targetFile && (!serialProcessing || isFirstOperation)) {
                        showToast('请选择要处理的文件', 'warning');
                        return null;
                    }
                    
                    // 在串行模式下，如果不是第一个操作且没有选择文件，这是正常的
                    if (serialProcessing && !isFirstOperation && !params.targetFile) {
                        // 不设置 targetFile，后端会自动使用前一步的输出
                        delete params.targetFile;
                    }
                }
            }
            
            switch (operationType) {
                case 'trim':
                    params.startTime = modal.querySelector('#startTime').value;
                    params.endTime = modal.querySelector('#endTime').value;
                    if (!params.startTime || !params.endTime) {
                        showToast('请填写开始时间和结束时间', 'warning');
                        return null;
                    }
                    break;
                case 'scale':
                    const scaleMode = modal.querySelector('#scaleMode').value;
                    params.scaleMode = scaleMode;
                    
                    if (scaleMode === 'dimensions') {
                        params.width = parseInt(modal.querySelector('#width').value) || null;
                        params.height = parseInt(modal.querySelector('#height').value) || null;
                        params.keepAspectRatio = modal.querySelector('#keepAspectRatio').checked;
                        if (!params.width && !params.height) {
                            showToast('请至少填写宽度或高度', 'warning');
                            return null;
                        }
                    } else {
                        params.scaleRatio = parseFloat(modal.querySelector('#scaleRatio').value);
                        if (!params.scaleRatio || params.scaleRatio <= 0) {
                            showToast('请填写有效的缩放比例', 'warning');
                            return null;
                        }
                    }
                    break;
                case 'watermark':
                    const watermarkType = modal.querySelector('#watermarkType').value;
                    params.watermarkType = watermarkType;
                    params.position = modal.querySelector('#position').value;
                    params.margin = parseInt(modal.querySelector('#margin').value) || 20;
                    
                    if (watermarkType === 'text') {
                        params.text = modal.querySelector('#text').value;
                        params.fontSize = parseInt(modal.querySelector('#fontSize').value);
                        params.color = modal.querySelector('#color').value;
                        if (!params.text) {
                            showToast('请填写水印文字', 'warning');
                            return null;
                        }
                    } else {
                        params.watermarkFile = modal.querySelector('#imagePath').value;
                        params.imageOpacity = parseFloat(modal.querySelector('#imageOpacity').value);
                        params.imageScale = parseFloat(modal.querySelector('#imageScale').value);
                        if (!params.watermarkFile) {
                            showToast('请填写水印图片路径', 'warning');
                            return null;
                        }
                    }
                    break;
                case 'mosaic':
                    // 获取马赛克处理方式
                    const processingModeElement = modal.querySelector('input[name="mosaicProcessingMode"]:checked');
                    params.processingMode = processingModeElement ? processingModeElement.value : 'pixel-based';
                    
                    // 获取马赛克区域
                    const mosaicRegions = [];
                    const regionElements = modal.querySelectorAll('.mosaic-region');
                    regionElements.forEach(regionEl => {
                        const top = parseInt(regionEl.querySelector('input[name="top"]').value) || 0;
                        const bottom = parseInt(regionEl.querySelector('input[name="bottom"]').value) || 0;
                        const left = parseInt(regionEl.querySelector('input[name="left"]').value) || 0;
                        const right = parseInt(regionEl.querySelector('input[name="right"]').value) || 0;
                        const thickness = parseInt(regionEl.querySelector('input[name="thickness"]').value) || 20;
                        
                        mosaicRegions.push({ top, bottom, left, right, thickness });
                    });
                    
                    if (mosaicRegions.length === 0) {
                        showToast('请至少设置一个马赛克区域', 'warning');
                        return null;
                    }
                    
                    // 获取时间段
                    const timeSegments = [];
                    const segmentElements = modal.querySelectorAll('.time-segment');
                    segmentElements.forEach(segmentEl => {
                        const start = segmentEl.querySelector('input[name="start"]').value.trim();
                        const end = segmentEl.querySelector('input[name="end"]').value.trim();
                        
                        // 只有当开始时间或结束时间有值时才添加时间段
                        if (start || end) {
                            timeSegments.push({ start: start || null, end: end || null });
                        }
                    });
                    
                    params.regions = mosaicRegions;
                    params.timeSegments = timeSegments.length > 0 ? timeSegments : null;
                    break;
                case 'merge':
                    params.mergeType = modal.querySelector('#mergeType').value;
                    // 获取选中的合并文件
                    params.filesToMerge = this.getSelectedMergeFiles();
                    
                    // 检查是否为串行处理模式
                    const serialProcessing = document.getElementById('serialProcessing').checked;
                    const isFirstOperation = this.workflow.length === 0;
                    const hasPreviousOutput = params.filesToMerge.includes('__PREVIOUS_OUTPUT__');
                    
                    // 在串行模式下，如果选择了前一步输出，至少需要再选择一个文件
                    if (serialProcessing && !isFirstOperation && hasPreviousOutput) {
                        const otherFiles = params.filesToMerge.filter(file => file !== '__PREVIOUS_OUTPUT__');
                        if (otherFiles.length < 1) {
                            showToast('选择了前一步输出后，还需要至少选择一个其他文件进行合并', 'warning');
                            return null;
                        }
                    } else {
                        // 普通模式或第一个操作，需要至少2个文件
                        if (params.filesToMerge.length < 2) {
                            showToast('请选择至少2个文件进行合并', 'warning');
                            return null;
                        }
                    }
                    break;
                case 'mirror':
                    params.direction = modal.querySelector('#direction').value;
                    break;
                case 'rotate':
                    params.angle = parseInt(modal.querySelector('#angle').value);
                    break;
                case 'speed':
                    params.factor = parseFloat(modal.querySelector('#factor').value);
                    if (params.factor <= 0) {
                        showToast('速度倍数必须大于0', 'warning');
                        return null;
                    }
                    break;
                case 'background':
                    // 获取选中的背景图片
                    params.backgroundImages = backgroundImageManager.getSelectedImages();
                    if (params.backgroundImages.length === 0) {
                        showToast('请至少选择一张背景图片', 'warning');
                        return null;
                    }
                    
                    // 获取缩放模式
                    params.scaleMode = modal.querySelector('#scaleMode').value;
                    break;
                case 'backgroundVideo':
                    // 获取背景视频文件
                    params.backgroundVideoFile = modal.querySelector('#backgroundVideoFile').value;
                    if (!params.backgroundVideoFile) {
                        showToast('请选择背景视频文件', 'warning');
                        return null;
                    }
                    
                    // 获取音频文件
                    params.audioFile = modal.querySelector('#audioFile').value;
                    if (!params.audioFile) {
                        showToast('请选择音频文件', 'warning');
                        return null;
                    }
                    break;
                case 'split':
                    params.splitMode = modal.querySelector('#splitMode').value;
                    
                    if (params.splitMode === 'fixed_length') {
                        params.segmentLength = parseInt(modal.querySelector('#segmentLength').value);
                        if (!params.segmentLength || params.segmentLength <= 0) {
                            showToast('请填写有效的每段时长', 'warning');
                            return null;
                        }
                        // 构建segments数组 - 固定长度分割模式
                        // 注意：这里我们只能提供分割长度，具体的时间段需要后端根据视频总时长计算
                        params.segments = [{
                            segmentLength: params.segmentLength,
                            mode: 'fixed_length'
                        }];
                    } else {
                        params.firstSegmentLength = parseInt(modal.querySelector('#firstSegmentLength').value);
                        if (!params.firstSegmentLength || params.firstSegmentLength <= 0) {
                            showToast('请填写有效的第一段时长', 'warning');
                            return null;
                        }
                        // 构建segments数组 - 指定第一段长度模式
                        // 将秒数转换为HH:MM:SS格式
                        const formatSecondsToTime = (seconds) => {
                            const hours = Math.floor(seconds / 3600);
                            const mins = Math.floor((seconds % 3600) / 60);
                            const secs = seconds % 60;
                            return `${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
                        };
                        
                        params.segments = [
                            {
                                startTime: '00:00:00',
                                endTime: formatSecondsToTime(params.firstSegmentLength),
                                name: 'part01'
                            },
                            {
                                startTime: formatSecondsToTime(params.firstSegmentLength),
                                endTime: null, // null表示到视频结束
                                name: 'part02'
                            }
                        ];
                    }
                    break;
                case 'subtitle':
                    // 获取字幕文件
                    const subtitleFileInput = modal.querySelector('#subtitleFile');
                    if (!subtitleFileInput.files || subtitleFileInput.files.length === 0) {
                        showToast('请选择字幕文件', 'warning');
                        return null;
                    }
                    
                    // 保存文件对象而不是路径
                    params.subtitleFile = subtitleFileInput.files[0];
                    params.subtitleFileName = subtitleFileInput.files[0].name;
                    
                    // 获取显示模式
                    params.displayMode = modal.querySelector('#displayMode').value;
                    
                    if (params.displayMode === 'fixed') {
                        params.frequency = parseFloat(modal.querySelector('#frequency').value);
                        if (!params.frequency || params.frequency <= 0) {
                            showToast('请填写有效的显示频率', 'warning');
                            return null;
                        }
                    }
                    
                    // 获取字体设置
                    params.fontSize = parseInt(modal.querySelector('#fontSize').value) || 24;
                    params.fontColor = modal.querySelector('#color').value || 'white';
                    params.position = modal.querySelector('#position').value || 'bottom';
                    
                    // 获取跳过空行设置
                    params.skipEmptyLines = modal.querySelector('#skipEmptyLines').checked;
                    break;
            }
            return params;
        } catch (error) {
            console.error('获取操作参数失败:', error);
            showToast('参数格式错误', 'error');
            return null;
        }
    },
    
    // 更新工作流显示
    updateWorkflowDisplay() {
        const workflowDiv = document.getElementById('operationWorkflow');
        
        if (this.workflow.length === 0) {
            workflowDiv.innerHTML = '<div class="no-operations">暂无操作，请点击上方按钮添加操作</div>';
            return;
        }
        
        const operationNames = {
            trim: '截取视频',
            scale: '缩放视频',
            watermark: '添加水印',
            mosaic: '添加马赛克',
            merge: '合并视频',
            background: '添加背景图',
            backgroundVideo: '添加背景视频',
            split: '分割视频',
            mirror: '镜像翻转',
            rotate: '旋转视频',
            speed: '调整速度',
            subtitle: '添加字幕'
        };
        
        let html = '';
        this.workflow.forEach((operation, index) => {
            html += `
                <div class="workflow-step">
                    <div class="step-header">
                        <div class="step-title">
                            <span class="step-number">${index + 1}</span>
                            ${operationNames[operation.type]}
                        </div>
                        <div class="step-controls">
                            <button class="btn btn-sm btn-secondary" onclick="videoProcessing.moveOperation(${index}, -1)" ${index === 0 ? 'disabled' : ''}>↑</button>
                            <button class="btn btn-sm btn-secondary" onclick="videoProcessing.moveOperation(${index}, 1)" ${index === this.workflow.length - 1 ? 'disabled' : ''}>↓</button>
                            <button class="btn btn-sm btn-danger" onclick="videoProcessing.removeOperation(${index})">删除</button>
                        </div>
                    </div>
                    <div class="step-params">
                        ${this.formatOperationParams(operation)}
                    </div>
                </div>
            `;
        });
        
        workflowDiv.innerHTML = html;
    },
    
    // 格式化操作参数显示
    formatOperationParams(operation) {
        const params = operation.params;
        let result = '';
        
        // 显示目标文件（除了合并操作）
        if (operation.type !== 'merge' && params.targetFile) {
            result += `目标文件: ${params.targetFile}<br>`;
        }
        
        switch (operation.type) {
            case 'trim':
                result += `开始时间: ${params.startTime}, 结束时间: ${params.endTime}`;
                break;
            case 'scale':
                if (params.scaleMode === 'dimensions') {
                    const dimensions = [];
                    if (params.width) dimensions.push(`宽度: ${params.width}`);
                    if (params.height) dimensions.push(`高度: ${params.height}`);
                    const aspectRatio = params.keepAspectRatio ? '保持宽高比' : '不保持宽高比';
                    result += `${dimensions.join(', ')}, ${aspectRatio}`;
                } else {
                    result += `缩放比例: ${params.scaleRatio}x`;
                }
                break;
            case 'watermark':
                if (params.watermarkType === 'text') {
                    result += `文字水印: "${params.text}", 位置: ${params.position}, 大小: ${params.fontSize}px, 颜色: ${params.color}`;
                } else {
                    result += `图片水印: ${params.watermarkFile}, 位置: ${params.position}, 透明度: ${params.imageOpacity}, 大小: ${params.imageScale}`;
                }
                result += `, 边距: ${params.margin}px`;
                break;
            case 'mosaic':
                result += `马赛克区域: ${params.regions.length}个`;
                if (params.timeSegments && params.timeSegments.length > 0) {
                    result += `, 时间段: ${params.timeSegments.length}个`;
                } else {
                    result += `, 时间段: 整个视频`;
                }
                break;
            case 'merge':
                const displayFiles = params.filesToMerge.map(file => 
                    file === '__PREVIOUS_OUTPUT__' ? '前一步输出' : file
                );
                result += `合并方式: ${params.mergeType}, 文件: ${displayFiles.join(', ')}`;
                break;
            case 'mirror':
                result += `镜像方向: ${params.direction}`;
                break;
            case 'rotate':
                result += `旋转角度: ${params.angle}°`;
                break;
            case 'speed':
                result += `速度倍数: ${params.factor}x`;
                break;
            case 'background':
                result += `背景图片: ${params.backgroundImages.length}张, 缩放模式: ${params.scaleMode}`;
                break;
            case 'backgroundVideo':
                result += `背景视频: ${params.backgroundVideoFile}, 音频文件: ${params.audioFile}`;
                break;
            case 'split':
                if (params.splitMode === 'fixed_length') {
                    result += `分割模式: 固定长度, 每段时长: ${params.segmentLength}秒`;
                } else {
                    result += `分割模式: 指定第一段, 第一段时长: ${params.firstSegmentLength}秒`;
                }
                break;
            case 'subtitle':
                result += `字幕文件: ${params.subtitleFile}`;
                if (params.displayMode === 'proportional') {
                    result += `, 显示模式: 按比例显示`;
                } else {
                    result += `, 显示模式: 固定频率 (${params.frequency}秒/行)`;
                }
                result += `, 字体大小: ${params.fontSize}px, 颜色: ${params.fontColor}, 位置: ${params.position}`;
                result += `, 跳过空行: ${params.skipEmptyLines ? '是' : '否'}`;
                break;
            default:
                result += '';
        }
        
        return result;
    },
    
    // 移动操作顺序
    moveOperation(index, direction) {
        const newIndex = index + direction;
        if (newIndex < 0 || newIndex >= this.workflow.length) return;
        
        const temp = this.workflow[index];
        this.workflow[index] = this.workflow[newIndex];
        this.workflow[newIndex] = temp;
        
        this.updateWorkflowDisplay();
    },
    
    // 移除操作
    removeOperation(index) {
        this.workflow.splice(index, 1);
        this.updateWorkflowDisplay();
        this.updateButtonStates();
    },
    
    // 清空工作流
    clearWorkflow() {
        this.workflow = [];
        this.updateWorkflowDisplay();
        this.updateButtonStates();
    },
    
    // 开始处理
    async startProcessing() {
        if (this.workflow.length === 0) {
            showToast('请添加处理操作', 'warning');
            return;
        }
        
        // 获取串行处理模式设置
        const serialProcessing = document.getElementById('serialProcessing').checked;
        
        let initialFiles;
        let validationResult;
        
        if (serialProcessing) {
            // 串行处理：只需要第一个操作的输入文件
            initialFiles = this.getInitialFilesFromWorkflow();
            
            if (initialFiles.length === 0) {
                showToast('请在第一个操作中指定要处理的文件', 'warning');
                return;
            }
            
            // 验证工作流的串行逻辑
            validationResult = this.validateSerialWorkflow();
            if (!validationResult.valid) {
                showToast(validationResult.message, 'warning');
                return;
            }
        } else {
            // 并行处理：收集所有操作中的文件
            initialFiles = this.getFilesFromWorkflow();
            
            if (initialFiles.length === 0) {
                showToast('工作流中没有指定要处理的文件', 'warning');
                return;
            }
        }

        // 保存处理开始状态到状态管理器
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.updateModuleState('videoProcessing', {
                isProcessing: true,
                processingProgress: 0,
                currentOperation: '准备处理...',
                workflow: this.workflow
            });
        }
        
        // 检查是否有文件需要上传
        const hasFileUploads = this.workflow.some(operation => 
            operation.params.subtitleFile instanceof File
        );
        
        let response;
        
        try {
            // 显示处理进度
            this.showProcessingProgress();
            
            if (hasFileUploads) {
                // 使用FormData处理文件上传
                const formData = new FormData();
                formData.append('videoFiles', JSON.stringify(initialFiles));
                formData.append('serialProcessing', serialProcessing);
                
                // 处理工作流中的文件上传
                const processedWorkflow = this.workflow.map((operation, index) => {
                    if (operation.params.subtitleFile instanceof File) {
                        // 将文件添加到FormData
                        const fileKey = `subtitleFile_${index}`;
                        formData.append(fileKey, operation.params.subtitleFile);
                        
                        // 修改操作参数，使用文件键而不是文件对象
                        return {
                            ...operation,
                            params: {
                                ...operation.params,
                                subtitleFile: fileKey,
                                subtitleFileName: operation.params.subtitleFileName
                            }
                        };
                    }
                    return operation;
                });
                
                formData.append('operations', JSON.stringify(processedWorkflow));
                
                response = await fetch('/api/video/process', {
                    method: 'POST',
                    body: formData
                });
            } else {
                // 没有文件上传，使用JSON
                const processingData = {
                    videoFiles: initialFiles,
                    operations: this.workflow,
                    serialProcessing: serialProcessing
                };
                
                response = await fetch('/api/video/process', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(processingData)
                });
            }
            
            const result = await response.json();
            
            if (response.ok) {
                this.currentWorkflowId = result.workflowId;
                this.monitorProcessing(result.workflowId);
                
                // 显示处理模式信息
                const mode = serialProcessing ? '串行' : '并行';
                showToast(`${mode}处理模式已启动`, 'info');
            } else {
                throw new Error(result.error || '处理失败');
            }
        } catch (error) {
            console.error('开始处理失败:', error);
            showToast('开始处理失败: ' + error.message, 'error');
            this.hideProcessingProgress();
            
            // 保存处理失败状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('videoProcessing', {
                    isProcessing: false,
                    processingProgress: 0,
                    currentOperation: null
                });
            }
        }
    },
    
    // 获取初始输入文件（第一个操作的文件）
    getInitialFilesFromWorkflow() {
        if (this.workflow.length === 0) return [];
        
        const firstOperation = this.workflow[0];
        const files = new Set();
        
        // 对于合并操作，使用所有要合并的文件
        if (firstOperation.type === 'merge' && firstOperation.params.filesToMerge) {
            firstOperation.params.filesToMerge.forEach(file => files.add(file));
        }
        // 对于背景视频操作，使用背景视频文件和音频文件
        else if (firstOperation.type === 'backgroundVideo') {
            if (firstOperation.params.backgroundVideoFile) {
                files.add(firstOperation.params.backgroundVideoFile);
            }
            if (firstOperation.params.audioFile) {
                files.add(firstOperation.params.audioFile);
            }
        }
        // 对于其他操作，使用目标文件
        else if (firstOperation.params.targetFile) {
            files.add(firstOperation.params.targetFile);
        }
        
        return Array.from(files);
    },
    
    // 验证串行工作流的逻辑
    validateSerialWorkflow() {
        if (this.workflow.length === 0) {
            return { valid: false, message: '工作流为空' };
        }
        
        // 检查第一个操作是否有输入文件
        const firstOperation = this.workflow[0];
        let hasInitialInput = false;
        
        if (firstOperation.type === 'merge' && firstOperation.params.filesToMerge && firstOperation.params.filesToMerge.length > 0) {
            hasInitialInput = true;
        } else if (firstOperation.type === 'backgroundVideo') {
            // 对于背景视频操作，检查是否有背景视频文件和音频文件
            hasInitialInput = firstOperation.params.backgroundVideoFile && firstOperation.params.audioFile;
        } else if (firstOperation.params.targetFile) {
            hasInitialInput = true;
        }
        
        if (!hasInitialInput) {
            return { 
                valid: false, 
                message: '第一个操作必须指定输入文件。串行处理中，后续操作将自动使用前一步的输出作为输入。' 
            };
        }
        
        // 检查后续的合并操作是否正确配置
        for (let i = 1; i < this.workflow.length; i++) {
            const operation = this.workflow[i];
            
            // 对于中间步骤的合并操作，检查是否包含前一步输出
            if (operation.type === 'merge') {
                const filesToMerge = operation.params.filesToMerge || [];
                const hasPreviousOutput = filesToMerge.includes('__PREVIOUS_OUTPUT__');
                
                if (!hasPreviousOutput) {
                    return { 
                        valid: false, 
                        message: `第${i + 1}个操作是合并操作，在串行处理中必须选择"使用前一步输出（推荐）"选项。` 
                    };
                }
                
                // 检查除了前一步输出外，是否还有其他文件
                const otherFiles = filesToMerge.filter(file => file !== '__PREVIOUS_OUTPUT__');
                if (otherFiles.length === 0) {
                    return { 
                        valid: false, 
                        message: `第${i + 1}个操作是合并操作，除了前一步输出外，还需要选择至少一个其他文件进行合并。` 
                    };
                }
            }
        }
        
        return { valid: true };
    },
    
    // 从工作流中获取所有要处理的文件（并行模式）
    getFilesFromWorkflow() {
        const files = new Set();
        
        this.workflow.forEach(operation => {
            if (operation.params.targetFile) {
                files.add(operation.params.targetFile);
            }
            if (operation.params.filesToMerge) {
                operation.params.filesToMerge.forEach(file => files.add(file));
            }
            // 处理背景视频操作的文件
            if (operation.type === 'backgroundVideo') {
                if (operation.params.backgroundVideoFile) {
                    files.add(operation.params.backgroundVideoFile);
                }
                if (operation.params.audioFile) {
                    files.add(operation.params.audioFile);
                }
            }
        });
        
        return Array.from(files);
    },
    
    // 显示处理进度
    showProcessingProgress() {
        const progressSection = document.getElementById('processingProgress');
        progressSection.style.display = 'block';
        
        const progressBar = document.getElementById('processingProgressFill');
        const progressText = document.getElementById('processingProgressText');
        const logContainer = document.getElementById('processingLogContainer');
        
        progressBar.style.width = '0%';
        progressBar.classList.remove('completed'); // 清除完成状态样式
        progressText.textContent = '准备处理...';
        progressText.classList.remove('completed'); // 清除完成状态样式
        logContainer.innerHTML = '';
        
        // 禁用相关按钮
        document.getElementById('startProcessingBtn').disabled = true;
    },
    
    // 重新启用处理按钮（保持日志可见）
    enableProcessingButton() {
        document.getElementById('startProcessingBtn').disabled = false;
        
        // 重置进度条和文本到初始状态，但保持日志区域可见
        const progressBar = document.getElementById('processingProgressFill');
        const progressText = document.getElementById('processingProgressText');
        if (progressBar) {
            progressBar.style.width = '0%';
            progressBar.style.background = ''; // 重置背景色
        }
        if (progressText) {
            progressText.textContent = '准备处理...';
            progressText.style.color = ''; // 重置文字颜色
        }
    },
    
    // 隐藏处理进度（仅在模式切换时使用）
    hideProcessingProgress() {
        document.getElementById('startProcessingBtn').disabled = false;
        document.getElementById('processingProgress').style.display = 'none';
        
        // 重置进度条和文本到初始状态
        const progressBar = document.getElementById('processingProgressFill');
        const progressText = document.getElementById('processingProgressText');
        if (progressBar) {
            progressBar.style.width = '0%';
            progressBar.style.background = ''; // 重置背景色
        }
        if (progressText) {
            progressText.textContent = '准备处理...';
            progressText.style.color = ''; // 重置文字颜色
        }
    },
    
    // 监控处理进度
    monitorProcessing(workflowId) {
        // 移除之前的监听器
        socket.off('video-process-progress');
        socket.off('video-process-complete');
        socket.off('video-process-error');
        
        // 添加新的监听器
        socket.on('video-process-progress', (data) => {
            if (data.workflowId === workflowId) {
                this.updateProcessingProgress(data);
            }
        });
        
        socket.on('video-process-complete', (data) => {
            if (data.workflowId === workflowId) {
                this.completeProcessing(data);
                // 移除监听器
                socket.off('video-process-progress');
                socket.off('video-process-complete');
                socket.off('video-process-error');
            }
        });
        
        socket.on('video-process-error', (data) => {
            if (data.workflowId === workflowId) {
                this.handleProcessingError(data);
                // 移除监听器
                socket.off('video-process-progress');
                socket.off('video-process-complete');
                socket.off('video-process-error');
            }
        });
    },
    
    // 更新处理进度
    updateProcessingProgress(data) {
        const progressBar = document.getElementById('processingProgressFill');
        const progressText = document.getElementById('processingProgressText');
        const logContainer = document.getElementById('processingLogContainer');
        
        // 进度更新
        if (data.progress !== undefined) {
            progressBar.style.width = `${data.progress}%`;
            progressText.textContent = `处理进度: ${data.progress}%`;
        }
        
        // 消息日志更新
        if (data.message) {
            // 检查是否是进度消息，避免重复显示进度
            const isProgressMessage = data.message.includes('处理进度:') || data.message.includes('进度:');
            
            // 如果是进度消息，检查是否已经有相同的进度消息
            if (isProgressMessage) {
                // 获取最后一条日志
                const lastLogEntry = logContainer.lastElementChild;
                
                // 如果最后一条日志也是进度消息，则更新它而不是添加新的
                if (lastLogEntry && lastLogEntry.textContent.includes('处理进度:')) {
                    lastLogEntry.textContent = `[${new Date().toLocaleTimeString()}] ${data.message}`;
                    logContainer.scrollTop = logContainer.scrollHeight;
                    return; // 更新后直接返回，不添加新日志
                }
            }
            
            // 如果不是进度消息或没有找到可更新的进度日志，则添加新日志
            const logEntry = document.createElement('div');
            logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${data.message}`;
            logContainer.appendChild(logEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
        }

        // 保存处理进度状态到状态管理器
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.updateModuleState('videoProcessing', {
                isProcessing: true,
                processingProgress: data.progress || 0,
                currentOperation: data.message || '处理中...',
                processingLogs: globalStateManager.getProcessingLogs()
            });
        }
    },
    
    // 完成处理
    completeProcessing(data) {
        const progressBar = document.getElementById('processingProgressFill');
        const progressText = document.getElementById('processingProgressText');
        const logContainer = document.getElementById('processingLogContainer');
        
        progressBar.style.width = '100%';
        progressBar.classList.add('completed'); // 添加完成样式
        progressText.textContent = '处理完成!';
        progressText.classList.add('completed'); // 添加完成样式
        
        const logEntry = document.createElement('div');
        logEntry.style.color = '#28a745';
        logEntry.style.fontWeight = 'bold';
        logEntry.style.fontSize = '14px';
        logEntry.style.padding = '5px';
        logEntry.style.backgroundColor = '#d4edda';
        logEntry.style.border = '1px solid #c3e6cb';
        logEntry.style.borderRadius = '4px';
        logEntry.style.marginTop = '5px';
        logEntry.textContent = `[${new Date().toLocaleTimeString()}] ✅ 处理完成! 输出文件: ${data.outputPath}`;
        logContainer.appendChild(logEntry);
        logContainer.scrollTop = logContainer.scrollHeight;
        
        // 添加完成后的操作提示
        const actionLogEntry = document.createElement('div');
        actionLogEntry.style.color = '#17a2b8';
        actionLogEntry.style.fontSize = '13px';
        actionLogEntry.style.padding = '8px';
        actionLogEntry.style.backgroundColor = '#d1ecf1';
        actionLogEntry.style.border = '1px solid #bee5eb';
        actionLogEntry.style.borderRadius = '4px';
        actionLogEntry.style.marginTop = '8px';
        actionLogEntry.innerHTML = `
            <div style="display: flex; align-items: center; gap: 8px;">
                <i class="fas fa-info-circle"></i>
                <span>处理完成！您可以继续查看日志或开始新的处理任务</span>
            </div>
        `;
        logContainer.appendChild(actionLogEntry);
        logContainer.scrollTop = logContainer.scrollHeight;
        
        // 保存处理完成状态到状态管理器
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.updateModuleState('videoProcessing', {
                isProcessing: false,
                processingProgress: 100,
                currentOperation: '处理完成',
                outputPath: data.outputPath
            });
        }
        
        // 处理完成后保持日志可见，不隐藏处理进度区域
        // 只重新启用开始处理按钮，让用户可以继续查看处理日志
        document.getElementById('startProcessingBtn').disabled = false;
        
        // 刷新文件列表
        this.loadDownloadedFiles();
        loadDownloadsList();
        
        // 显示成功提示
        showToast('🎉 视频处理完成！日志已保留供查看', 'success', 4000);
    },
    
    // 处理错误
    handleProcessingError(data) {
        const progressSection = document.getElementById('processingProgress');
        const logContainer = document.getElementById('processingLogContainer');
        const progressBar = document.getElementById('processingProgressFill');
        const progressText = document.getElementById('processingProgressText');
        
        // 确保处理进度区域可见
        if (progressSection) {
            progressSection.style.display = 'block';
        }
        
        // 更新进度条为错误状态
        if (progressBar) {
            progressBar.style.width = '100%';
            progressBar.style.background = 'linear-gradient(90deg, #dc3545, #c82333)';
        }
        
        if (progressText) {
            progressText.textContent = '处理失败!';
            progressText.style.color = '#dc3545';
        }
        
        // 在日志容器中显示错误信息
        if (logContainer) {
            const logEntry = document.createElement('div');
            logEntry.style.color = '#721c24';
            logEntry.style.fontWeight = 'bold';
            logEntry.style.fontSize = '14px';
            logEntry.style.padding = '10px';
            logEntry.style.backgroundColor = '#f8d7da';
            logEntry.style.border = '2px solid #f5c6cb';
            logEntry.style.borderRadius = '6px';
            logEntry.style.marginTop = '10px';
            logEntry.style.boxShadow = '0 2px 8px rgba(220, 53, 69, 0.15)';
            logEntry.textContent = `[${new Date().toLocaleTimeString()}] ❌ 处理失败: ${data.error}`;
            logContainer.appendChild(logEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
        }
        
        // 显示Toast错误提示
        showToast(`视频处理失败: ${data.error}`, 'error', 5000);
        
        // 保持日志可见，只重新启用处理按钮
        document.getElementById('startProcessingBtn').disabled = false;
        
        // 添加错误后的操作提示
        if (logContainer) {
            const actionLogEntry = document.createElement('div');
            actionLogEntry.style.color = '#856404';
            actionLogEntry.style.fontSize = '13px';
            actionLogEntry.style.padding = '8px';
            actionLogEntry.style.backgroundColor = '#fff3cd';
            actionLogEntry.style.border = '1px solid #ffeaa7';
            actionLogEntry.style.borderRadius = '4px';
            actionLogEntry.style.marginTop = '8px';
            actionLogEntry.innerHTML = `
                <div style="display: flex; align-items: center; gap: 8px;">
                    <i class="fas fa-exclamation-triangle"></i>
                    <span>处理失败！请检查上述错误信息，修正后可重新尝试</span>
                </div>
            `;
            logContainer.appendChild(actionLogEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
        }
    },

    // 添加马赛克区域
    addMosaicRegion() {
        const regionsContainer = document.getElementById('mosaicRegions');
        const regionCount = regionsContainer.children.length;
        const newRegionId = regionCount;
        
        const regionDiv = document.createElement('div');
        regionDiv.className = 'mosaic-region';
        regionDiv.setAttribute('data-region-id', newRegionId);
        
        regionDiv.innerHTML = `
            <h4>区域 ${regionCount + 1}</h4>
            <div class="region-params">
                <div class="param-row">
                    <div class="param-col">
                        <label>距离顶部 (像素)</label>
                        <input type="number" name="top" value="50" min="0" required>
                    </div>
                    <div class="param-col">
                        <label>距离底部 (像素)</label>
                        <input type="number" name="bottom" value="50" min="0" required>
                    </div>
                </div>
                <div class="param-row">
                    <div class="param-col">
                        <label>距离左侧 (像素)</label>
                        <input type="number" name="left" value="50" min="0" required>
                    </div>
                    <div class="param-col">
                        <label>距离右侧 (像素)</label>
                        <input type="number" name="right" value="50" min="0" required>
                    </div>
                </div>
                <div class="param-row">
                    <div class="param-col">
                        <label>马赛克厚度</label>
                        <input type="number" name="thickness" value="20" min="5" max="100" required>
                        <small>数值越大，马赛克块越大</small>
                    </div>
                </div>
            </div>
            <button type="button" class="btn btn-danger btn-sm remove-region" onclick="videoProcessing.removeMosaicRegion(${newRegionId})">删除区域</button>
        `;
        
        regionsContainer.appendChild(regionDiv);
        this.updateMosaicRegionButtons();
    },

    // 删除马赛克区域
    removeMosaicRegion(regionId) {
        const regionsContainer = document.getElementById('mosaicRegions');
        const regionToRemove = regionsContainer.querySelector(`[data-region-id="${regionId}"]`);
        
        if (regionToRemove && regionsContainer.children.length > 1) {
            regionToRemove.remove();
            this.updateMosaicRegionButtons();
            this.reorderMosaicRegions();
        }
    },

    // 更新马赛克区域按钮状态
    updateMosaicRegionButtons() {
        const regionsContainer = document.getElementById('mosaicRegions');
        const regions = regionsContainer.querySelectorAll('.mosaic-region');
        
        regions.forEach((region, index) => {
            const removeBtn = region.querySelector('.remove-region');
            if (removeBtn) {
                removeBtn.style.display = regions.length > 1 ? 'inline-block' : 'none';
            }
        });
    },

    // 重新排序马赛克区域
    reorderMosaicRegions() {
        const regionsContainer = document.getElementById('mosaicRegions');
        const regions = regionsContainer.querySelectorAll('.mosaic-region');
        
        regions.forEach((region, index) => {
            region.setAttribute('data-region-id', index);
            const title = region.querySelector('h4');
            if (title) {
                title.textContent = `区域 ${index + 1}`;
            }
            const removeBtn = region.querySelector('.remove-region');
            if (removeBtn) {
                removeBtn.setAttribute('onclick', `videoProcessing.removeMosaicRegion(${index})`);
            }
        });
    },

    // 添加马赛克时间段
    addMosaicTimeSegment() {
        const segmentsContainer = document.getElementById('mosaicTimeSegments');
        const segmentCount = segmentsContainer.children.length;
        const newSegmentId = segmentCount;
        
        const segmentDiv = document.createElement('div');
        segmentDiv.className = 'time-segment';
        segmentDiv.setAttribute('data-segment-id', newSegmentId);
        
        segmentDiv.innerHTML = `
            <h4>时间段 ${segmentCount + 1}</h4>
            <div class="param-row">
                <div class="param-col">
                    <label>开始时间 (格式: 00:01:30)</label>
                    <input type="text" name="start" placeholder="00:00:00" pattern="[0-9]{2}:[0-9]{2}:[0-9]{2}">
                    <small>留空表示从视频开始</small>
                </div>
                <div class="param-col">
                    <label>结束时间 (格式: 00:02:30)</label>
                    <input type="text" name="end" placeholder="00:01:00" pattern="[0-9]{2}:[0-9]{2}:[0-9]{2}">
                    <small>留空表示到视频结束</small>
                </div>
            </div>
            <button type="button" class="btn btn-danger btn-sm remove-segment" onclick="videoProcessing.removeMosaicTimeSegment(${newSegmentId})">删除时间段</button>
        `;
        
        segmentsContainer.appendChild(segmentDiv);
        this.updateMosaicTimeSegmentButtons();
    },

    // 删除马赛克时间段
    removeMosaicTimeSegment(segmentId) {
        const segmentsContainer = document.getElementById('mosaicTimeSegments');
        const segmentToRemove = segmentsContainer.querySelector(`[data-segment-id="${segmentId}"]`);
        
        if (segmentToRemove && segmentsContainer.children.length > 1) {
            segmentToRemove.remove();
            this.updateMosaicTimeSegmentButtons();
            this.reorderMosaicTimeSegments();
        }
    },

    // 更新马赛克时间段按钮状态
    updateMosaicTimeSegmentButtons() {
        const segmentsContainer = document.getElementById('mosaicTimeSegments');
        const segments = segmentsContainer.querySelectorAll('.time-segment');
        
        segments.forEach((segment, index) => {
            const removeBtn = segment.querySelector('.remove-segment');
            if (removeBtn) {
                removeBtn.style.display = segments.length > 1 ? 'inline-block' : 'none';
            }
        });
    },

    // 重新排序马赛克时间段
    reorderMosaicTimeSegments() {
        const segmentsContainer = document.getElementById('mosaicTimeSegments');
        const segments = segmentsContainer.querySelectorAll('.time-segment');
        
        segments.forEach((segment, index) => {
            segment.setAttribute('data-segment-id', index);
            const title = segment.querySelector('h4');
            if (title) {
                title.textContent = `时间段 ${index + 1}`;
            }
            const removeBtn = segment.querySelector('.remove-segment');
            if (removeBtn) {
                removeBtn.setAttribute('onclick', `videoProcessing.removeMosaicTimeSegment(${index})`);
            }
        });
    },

    // 手动预览视频
    async manualPreviewVideo() {
        const selectedFileInfo = document.querySelector('.selected-file-info');
        if (!selectedFileInfo) {
            alert('请先选择一个视频文件');
            return;
        }

        const filename = selectedFileInfo.textContent.trim();
        if (!filename || !this.isVideoFile(filename)) {
            alert('请选择一个有效的视频文件');
            return;
        }

        // 调用现有的预览方法
        await this.loadVideoPreview(filename);
    },

    // 加载视频预览
    async loadVideoPreview(filePath) {
        const previewContainer = document.getElementById('videoPreviewContainer');
        if (!previewContainer) return;

        try {
            // 显示加载状态
            previewContainer.innerHTML = `
                <div class="video-preview-loading" style="
                    width: 100%;
                    height: 300px;
                    background: #f5f5f5;
                    border: 2px dashed #ddd;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 8px;
                    color: #666;
                    font-size: 14px;
                ">
                    <div style="text-align: center;">
                        <i class="fas fa-spinner fa-spin" style="font-size: 24px; margin-bottom: 10px;"></i>
                        <p>正在生成视频预览...</p>
                    </div>
                </div>
            `;

            // 获取视频信息
            const videoInfoResponse = await fetch('/api/video-info', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ filePath })
            });

            if (!videoInfoResponse.ok) {
                throw new Error('获取视频信息失败');
            }

            const videoInfo = await videoInfoResponse.json();

            // 根据视频实际比例计算预览图尺寸
            const maxWidth = 640;  // 最大宽度
            const maxHeight = 480; // 最大高度
            
            let previewWidth, previewHeight;
            const aspectRatio = videoInfo.width / videoInfo.height;
            
            if (aspectRatio > 1) {
                // 横屏视频
                previewWidth = Math.min(maxWidth, videoInfo.width);
                previewHeight = Math.round(previewWidth / aspectRatio);
            } else {
                // 竖屏视频
                previewHeight = Math.min(maxHeight, videoInfo.height);
                previewWidth = Math.round(previewHeight * aspectRatio);
            }

            // 生成预览图
            const previewResponse = await fetch('/api/video-preview', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ 
                    filePath,
                    timestamp: 10,
                    width: previewWidth,
                    height: previewHeight
                })
            });

            if (!previewResponse.ok) {
                throw new Error('生成预览图失败');
            }

            const previewData = await previewResponse.json();

            // 显示预览图和可视化选择界面
            this.displayVideoPreview(previewData.previewPath, videoInfo);

        } catch (error) {
            console.error('加载视频预览失败:', error);
            previewContainer.innerHTML = `
                <div class="video-preview-error" style="
                    width: 100%;
                    height: 300px;
                    background: #fff5f5;
                    border: 2px dashed #ff6b6b;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 8px;
                    color: #d63031;
                    font-size: 14px;
                ">
                    <div style="text-align: center;">
                        <i class="fas fa-exclamation-triangle" style="font-size: 24px; margin-bottom: 10px;"></i>
                        <p>预览加载失败</p>
                        <p style="font-size: 12px;">${error.message}</p>
                    </div>
                </div>
            `;
        }
    },

    // 显示视频预览和可视化选择界面
    displayVideoPreview(previewPath, videoInfo) {
        const previewContainer = document.getElementById('videoPreviewContainer');
        if (!previewContainer) return;

        // 确保预览图片路径包含正确的服务器URL前缀
        const fullPreviewPath = previewPath.startsWith('http') ? previewPath : `${window.location.origin}/${previewPath}`;

        const canvasId = `mosaicCanvas_${Date.now()}`;
        
        // 计算容器的最大宽度，根据视频比例调整
        const aspectRatio = videoInfo.width / videoInfo.height;
        let maxWidth;
        
        if (aspectRatio > 1) {
            // 横屏视频
            maxWidth = '640px';
        } else {
            // 竖屏视频，使用较小的最大宽度
            maxWidth = '360px';
        }
        
        previewContainer.innerHTML = `
            <div class="video-preview-wrapper" style="position: relative; width: 100%; max-width: ${maxWidth}; margin: 0 auto;">
                <canvas id="${canvasId}" 
                        width="${videoInfo.width}" 
                        height="${videoInfo.height}"
                        style="
                            width: 100%;
                            height: auto;
                            border: 1px solid #ddd;
                            border-radius: 8px;
                            cursor: crosshair;
                            background-image: url('${fullPreviewPath}');
                            background-size: cover;
                            background-position: center;
                            background-repeat: no-repeat;
                        ">
                </canvas>
                <div class="video-info" style="
                    margin-top: 10px;
                    padding: 10px;
                    background: #f8f9fa;
                    border-radius: 6px;
                    font-size: 12px;
                    color: #666;
                ">
                    <div style="display: flex; justify-content: space-between; flex-wrap: wrap;">
                        <span>分辨率: ${videoInfo.width}x${videoInfo.height}</span>
                        <span>时长: ${this.formatDuration(videoInfo.duration)}</span>
                        <span>帧率: ${videoInfo.fps}fps</span>
                        <span>比例: ${aspectRatio > 1 ? '横屏' : '竖屏'} (${aspectRatio.toFixed(2)}:1)</span>
                    </div>
                </div>
                <div class="selection-instructions" style="
                    margin-top: 10px;
                    padding: 8px 12px;
                    background: #e3f2fd;
                    border-radius: 6px;
                    font-size: 12px;
                    color: #1976d2;
                ">
                    <i class="fas fa-info-circle" style="margin-right: 6px;"></i>
                    在预览图上拖拽鼠标选择马赛克区域，选择后会自动更新坐标参数
                </div>
            </div>
        `;

        // 初始化画布选择功能
        this.initCanvasSelection(canvasId, videoInfo);
    },

    // 初始化画布选择功能
    initCanvasSelection(canvasId, videoInfo) {
        const canvas = document.getElementById(canvasId);
        if (!canvas) return;

        const ctx = canvas.getContext('2d');
        let isDrawing = false;
        let startX, startY, endX, endY;
        let currentSelection = null;

        // 鼠标事件处理
        canvas.addEventListener('mousedown', (e) => {
            const rect = canvas.getBoundingClientRect();
            const scaleX = canvas.width / rect.width;
            const scaleY = canvas.height / rect.height;
            
            startX = (e.clientX - rect.left) * scaleX;
            startY = (e.clientY - rect.top) * scaleY;
            isDrawing = true;
        });

        canvas.addEventListener('mousemove', (e) => {
            if (!isDrawing) return;

            const rect = canvas.getBoundingClientRect();
            const scaleX = canvas.width / rect.width;
            const scaleY = canvas.height / rect.height;
            
            endX = (e.clientX - rect.left) * scaleX;
            endY = (e.clientY - rect.top) * scaleY;

            // 清除画布并重绘选择框
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            this.drawSelectionBox(ctx, startX, startY, endX, endY);
        });

        canvas.addEventListener('mouseup', (e) => {
            if (!isDrawing) return;
            isDrawing = false;

            const rect = canvas.getBoundingClientRect();
            const scaleX = canvas.width / rect.width;
            const scaleY = canvas.height / rect.height;
            
            endX = (e.clientX - rect.left) * scaleX;
            endY = (e.clientY - rect.top) * scaleY;

            // 计算选择区域的坐标
            const left = Math.min(startX, endX);
            const top = Math.min(startY, endY);
            const right = Math.max(startX, endX);
            const bottom = Math.max(startY, endY);

            // 更新当前活跃区域的坐标
            this.updateMosaicRegionCoordinates(Math.round(left), Math.round(top), Math.round(right), Math.round(bottom));

            // 保存当前选择
            currentSelection = { left, top, right, bottom };
            
            // 重绘最终选择框
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            this.drawSelectionBox(ctx, startX, startY, endX, endY, true);
        });
    },

    // 绘制选择框
    drawSelectionBox(ctx, x1, y1, x2, y2, isFinal = false) {
        const left = Math.min(x1, x2);
        const top = Math.min(y1, y2);
        const width = Math.abs(x2 - x1);
        const height = Math.abs(y2 - y1);

        // 绘制选择框
        ctx.strokeStyle = isFinal ? '#ff4757' : '#3742fa';
        ctx.lineWidth = 2;
        ctx.setLineDash(isFinal ? [] : [5, 5]);
        ctx.strokeRect(left, top, width, height);

        // 绘制半透明填充
        ctx.fillStyle = isFinal ? 'rgba(255, 71, 87, 0.2)' : 'rgba(55, 66, 250, 0.1)';
        ctx.fillRect(left, top, width, height);

        // 绘制坐标信息
        if (isFinal) {
            ctx.fillStyle = '#ff4757';
            ctx.font = '12px Arial';
            ctx.fillText(`${Math.round(width)}x${Math.round(height)}`, left + 5, top + 15);
        }
    },

    // 更新马赛克区域坐标
    updateMosaicRegionCoordinates(left, top, right, bottom) {
        // 获取当前活跃的区域（最后一个区域）
        const regions = document.querySelectorAll('#mosaicRegions .mosaic-region');
        const activeRegion = regions[regions.length - 1];
        
        if (activeRegion) {
            const leftInput = activeRegion.querySelector('input[name="left"]');
            const topInput = activeRegion.querySelector('input[name="top"]');
            const rightInput = activeRegion.querySelector('input[name="right"]');
            const bottomInput = activeRegion.querySelector('input[name="bottom"]');

            if (leftInput) leftInput.value = left;
            if (topInput) topInput.value = top;
            if (rightInput) rightInput.value = right;
            if (bottomInput) bottomInput.value = bottom;

            // 显示成功提示
            showToast('马赛克区域坐标已更新', 'success', 2000);
        }
    },

    // 格式化时长
    formatDuration(seconds) {
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        
        if (hours > 0) {
            return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        } else {
            return `${minutes}:${secs.toString().padStart(2, '0')}`;
        }
    },

    // 背景图相关方法
    showBackgroundImageSelector() {
        backgroundImageManager.showImageSelector();
    },

    clearBackgroundImages() {
        backgroundImageManager.clearImages();
    }
};

// 自定义确认对话框
function showCustomConfirm(title, message) {
    return new Promise((resolve) => {
        if (!elements.customConfirmModal) {
            // 如果没有自定义对话框，使用原生confirm
            resolve(confirm(message));
            return;
        }
        
        elements.confirmTitle.textContent = title;
        elements.confirmMessage.textContent = message;
        elements.customConfirmModal.style.display = 'flex';
        
        // 移除之前的事件监听器
        const newCancelBtn = elements.confirmCancelBtn.cloneNode(true);
        const newOkBtn = elements.confirmOkBtn.cloneNode(true);
        elements.confirmCancelBtn.parentNode.replaceChild(newCancelBtn, elements.confirmCancelBtn);
        elements.confirmOkBtn.parentNode.replaceChild(newOkBtn, elements.confirmOkBtn);
        elements.confirmCancelBtn = newCancelBtn;
        elements.confirmOkBtn = newOkBtn;
        
        // 添加事件监听器
        elements.confirmCancelBtn.addEventListener('click', () => {
            elements.customConfirmModal.style.display = 'none';
            resolve(false);
        });
        
        elements.confirmOkBtn.addEventListener('click', () => {
            elements.customConfirmModal.style.display = 'none';
            resolve(true);
        });
        
        // 点击背景关闭
        elements.customConfirmModal.addEventListener('click', (e) => {
            if (e.target === elements.customConfirmModal) {
                elements.customConfirmModal.style.display = 'none';
                resolve(false);
            }
        });
        
        // ESC键关闭
        const handleEsc = (e) => {
            if (e.key === 'Escape') {
                elements.customConfirmModal.style.display = 'none';
                document.removeEventListener('keydown', handleEsc);
                resolve(false);
            }
        };
        document.addEventListener('keydown', handleEsc);
    });
}

// Toast 消息显示
function showToast(message, type = 'info', duration = 3000) {
    // 创建 toast 元素
    const toast = document.createElement('div');
    toast.className = `toast ${type}`;
    toast.innerHTML = `
        <i class="fas ${getToastIcon(type)}"></i>
        <span>${message}</span>
    `;
    
    // 计算当前已有的toast数量，调整位置
    const existingToasts = document.querySelectorAll('body > .toast');
    let topOffset = 20;
    existingToasts.forEach((existingToast, index) => {
        topOffset += existingToast.offsetHeight + 16;
    });
    
    // 设置位置
    toast.style.top = `${topOffset}px`;
    
    // 添加到页面
    document.body.appendChild(toast);
    
    // 显示动画
    setTimeout(() => {
        toast.classList.add('show');
    }, 100);
    
    // 自动隐藏
    setTimeout(() => {
        toast.classList.remove('show');
        setTimeout(() => {
            if (toast.parentNode) {
                toast.parentNode.removeChild(toast);
                // 重新调整剩余toast的位置
                adjustToastPositions();
            }
        }, 400);
    }, duration);
}

// 调整toast位置的辅助函数
function adjustToastPositions() {
    const toasts = document.querySelectorAll('body > .toast');
    let topOffset = 20;
    toasts.forEach((toast, index) => {
        toast.style.top = `${topOffset}px`;
        topOffset += toast.offsetHeight + 16;
    });
}

function getToastIcon(type) {
    switch (type) {
        case 'success': return 'fa-check-circle';
        case 'error': return 'fa-times-circle';
        case 'warning': return 'fa-exclamation-triangle';
        case 'info':
        default: return 'fa-info-circle';
    }
}

// 搜索结果翻页功能
function updateSearchPagination() {
    if (!elements.paginationContainer) return;
    
    const { currentPage, totalPages, totalItems } = appState.searchPagination;
    
    if (totalPages <= 1) {
        elements.paginationContainer.style.display = 'none';
        return;
    }
    
    elements.paginationContainer.style.display = 'flex';
    elements.prevPageBtn.disabled = currentPage <= 1;
    elements.nextPageBtn.disabled = currentPage >= totalPages;
    
    if (elements.currentPageSpan) {
        elements.currentPageSpan.textContent = currentPage;
    }
    if (elements.totalPagesSpan) {
        elements.totalPagesSpan.textContent = totalPages;
    }
}

function goToPreviousPage() {
    if (appState.searchPagination.currentPage > 1) {
        appState.searchPagination.currentPage--;
        displaySearchResults();
        updateSearchPagination();
    }
}

function goToNextPage() {
    if (appState.searchPagination.currentPage < appState.searchPagination.totalPages) {
        appState.searchPagination.currentPage++;
        displaySearchResults();
        updateSearchPagination();
    }
}

// 显示搜索结果（支持分页）
function displaySearchResults() {
    if (!appState.searchResults || appState.searchResults.length === 0) {
        elements.searchResultsList.innerHTML = '<div class="search-empty">没有找到相关视频</div>';
        elements.paginationContainer.style.display = 'none';
        return;
    }
    
    const { currentPage, itemsPerPage } = appState.searchPagination;
    const startIndex = (currentPage - 1) * itemsPerPage;
    const endIndex = startIndex + itemsPerPage;
    const pageResults = appState.searchResults.slice(startIndex, endIndex);
    
    elements.searchResultsList.innerHTML = '';
    
    pageResults.forEach((video, index) => {
        const actualIndex = startIndex + index;
        const item = createSearchResultItem(video, actualIndex);
        elements.searchResultsList.appendChild(item);
    });
    
    updateSearchPagination();
    updateSelectedCount();
}

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
    // 首先初始化全局状态管理器
    globalStateManager.init();
    
    init();
    videoProcessing.init();
    outputManager.init();
    backgroundManager.init();
    settingsManager.init();
    cleanupManager.init();
    savedLinksManager.init();
});

// 产出物管理功能
const outputManager = {
    outputs: [],
    
    init() {
        this.loadOutputs();
        this.bindEvents();
        this.updateDisplay();
    },
    
    bindEvents() {
        // 刷新按钮
        const refreshBtn = document.getElementById('refreshOutputsBtn');
        if (refreshBtn) {
            refreshBtn.addEventListener('click', () => {
                this.loadOutputs();
                showToast('产出物列表已刷新', 'success');
            });
        }
        
        // 清空按钮
        const clearBtn = document.getElementById('clearOutputsBtn');
        if (clearBtn) {
            clearBtn.addEventListener('click', async () => {
                const confirmed = await showCustomConfirm('确认清空', '确定要清空所有产出物记录吗？这将删除所有产出物文件！');
                if (confirmed) {
                    this.clearAllOutputs();
                }
            });
        }
    },
    
    // 添加新的产出物
    addOutput(outputData) {
        const output = {
            id: Date.now() + Math.random(),
            filename: outputData.filename,
            filepath: outputData.filepath,
            originalFile: outputData.originalFile,
            operations: outputData.operations || [],
            createdAt: new Date().toISOString(),
            fileSize: outputData.fileSize || 0,
            duration: outputData.duration || 0
        };
        
        this.outputs.unshift(output); // 添加到开头
        this.saveOutputs();
        this.updateDisplay();
        
        showToast(`产出物 "${output.filename}" 已添加`, 'success');
    },
    
    // 删除产出物
    async deleteOutput(outputId) {
        const output = this.outputs.find(o => o.id === outputId);
        if (!output) return;
        
        const confirmed = await showCustomConfirm('确认删除', `确定要删除产出物 "${output.filename}" 吗？`);
        if (!confirmed) return;
        
        try {
            // 发送删除请求到服务器
            const response = await fetch('/api/delete-output', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ filepath: output.filepath })
            });
            
            if (response.ok) {
                this.outputs = this.outputs.filter(o => o.id !== outputId);
                this.saveOutputs();
                this.updateDisplay();
                showToast(`产出物 "${output.filename}" 已删除`, 'success');
            } else {
                throw new Error('删除失败');
            }
        } catch (error) {
            console.error('删除产出物失败:', error);
            showToast('删除产出物失败', 'error');
        }
    },
    
    // 下载产出物
    downloadOutput(outputId) {
        const output = this.outputs.find(o => o.id === outputId);
        if (!output) return;
        
        const link = document.createElement('a');
        link.href = `/download/${encodeURIComponent(output.filename)}`;
        link.download = output.filename;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
        showToast(`开始下载 "${output.filename}"`, 'info');
    },
    
    // 显示产出物详情
    showOutputDetail(outputId) {
        const output = this.outputs.find(o => o.id === outputId);
        if (!output) return;
        
        // 创建模态框
        const modal = document.createElement('div');
        modal.className = 'output-detail-modal';
        modal.innerHTML = `
            <div class="output-detail-content">
                <div class="output-detail-header">
                    <h3><i class="fas fa-info-circle"></i> 产出物详情</h3>
                    <button class="output-detail-close">&times;</button>
                </div>
                <div class="output-detail-body">
                    <div class="detail-section">
                        <h4><i class="fas fa-file-video"></i> 基本信息</h4>
                        <div class="detail-content">
                            <p><strong>文件名:</strong> ${output.filename}</p>
                            <p><strong>原始文件:</strong> ${output.originalFile || '未知'}</p>
                            <p><strong>创建时间:</strong> ${new Date(output.createdAt).toLocaleString()}</p>
                            <p><strong>文件大小:</strong> ${this.formatFileSize(output.fileSize)}</p>
                            ${output.duration ? `<p><strong>视频时长:</strong> ${this.formatDuration(output.duration)}</p>` : ''}
                        </div>
                    </div>
                    <div class="detail-section">
                        <h4><i class="fas fa-cogs"></i> 处理操作</h4>
                        <div class="detail-content">
                            ${this.renderOperationDetails(output.operations)}
                        </div>
                    </div>
                </div>
            </div>
        `;
        
        document.body.appendChild(modal);
        
        // 绑定关闭事件
        const closeBtn = modal.querySelector('.output-detail-close');
        const closeModal = () => {
            document.body.removeChild(modal);
        };
        
        closeBtn.addEventListener('click', closeModal);
        modal.addEventListener('click', (e) => {
            if (e.target === modal) closeModal();
        });
        
        // ESC键关闭
        const handleEsc = (e) => {
            if (e.key === 'Escape') {
                closeModal();
                document.removeEventListener('keydown', handleEsc);
            }
        };
        document.addEventListener('keydown', handleEsc);
    },
    
    // 渲染操作详情
    renderOperationDetails(operations) {
        if (!operations || operations.length === 0) {
            return '<p>无处理操作记录</p>';
        }
        
        return operations.map(op => `
            <div class="operation-detail">
                <h5><i class="fas fa-play"></i> ${this.getOperationName(op.type)}</h5>
                <div class="operation-params">
                    ${this.formatOperationParams(op)}
                </div>
            </div>
        `).join('');
    },
    
    // 获取操作名称
    getOperationName(type) {
        const names = {
            'trim': '截取视频',
            'scale': '缩放视频',
            'merge': '合并视频',
            'mosaic': '添加马赛克',
            'background': '添加背景图',
            'backgroundVideo': '添加背景视频',
            'watermark': '添加水印',
            'mirror': '镜像翻转',
            'rotate': '旋转视频',
            'speed': '调整速度',
            'split': '分割视频',
            'subtitle': '添加字幕',
            'extract-audio': '提取音频',
            'add-subtitle': '添加字幕'
        };
        return names[type] || type;
    },
    
    // 格式化操作参数
    formatOperationParams(operation) {
        switch (operation.type) {
            case 'trim':
                return `开始时间: ${operation.params.start || '00:00:00'}, 结束时间: ${operation.params.end || '视频结束'}`;
            case 'scale':
                return `目标尺寸: ${operation.params.width}x${operation.params.height}`;
            case 'mosaic':
                const regions = operation.params.regions || [];
                const timeSegments = operation.params.timeSegments || [];
                let result = `马赛克区域: ${regions.length}个`;
                if (timeSegments.length > 0) {
                    result += `, 时间段: ${timeSegments.length}个`;
                } else {
                    result += ', 应用于整个视频';
                }
                return result;
            case 'merge':
                return `合并文件数: ${operation.params.files ? operation.params.files.length : 0}个`;
            case 'split':
                if (operation.params.splitMode === 'fixed_length') {
                    return `分割模式: 固定长度, 每段时长: ${operation.params.segmentLength}秒`;
                } else {
                    return `分割模式: 指定第一段, 第一段时长: ${operation.params.firstSegmentLength}秒`;
                }
            case 'mirror':
                return `镜像方向: ${operation.params.direction === 'horizontal' ? '水平镜像' : '垂直镜像'}`;
            case 'rotate':
                return `旋转角度: ${operation.params.angle}度`;
            case 'speed':
                return `播放速度: ${operation.params.speed}x`;
            case 'background':
                return `背景图片: ${operation.params.backgroundImage}`;
            case 'backgroundVideo':
                return `背景视频: ${operation.params.backgroundVideo}`;
            case 'watermark':
                if (operation.params.watermarkType === 'text') {
                    return `文字水印: "${operation.params.text}", 位置: ${operation.params.position}`;
                } else {
                    return `图片水印: ${operation.params.watermarkImage}, 位置: ${operation.params.position}`;
                }
            case 'subtitle':
                return `字幕文件: ${operation.params.subtitleFile}`;
            default:
                return JSON.stringify(operation.params || {});
        }
    },
    
    // 格式化文件大小
    formatFileSize(bytes) {
        if (!bytes) return '未知';
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(1024));
        return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
    },
    
    // 格式化时长
    formatDuration(seconds) {
        if (!seconds) return '未知';
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    },
    
    // 清空所有产出物
    async clearAllOutputs() {
        try {
            const response = await fetch('/api/clear-outputs', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            
            const result = await response.json();
            
            if (response.ok && result.success) {
                this.outputs = [];
                this.saveOutputs();
                this.updateDisplay();
                
                // 显示详细的清空结果
                let message = result.message || '所有产出物已清空';
                if (result.deletedFiles !== undefined && result.deletedDirs !== undefined) {
                    message = `清空完成：删除了 ${result.deletedFiles} 个文件和 ${result.deletedDirs} 个目录`;
                }
                
                showToast(message, 'success');
                
                // 如果有警告信息，显示详细信息
                if (result.warnings && result.warnings.length > 0) {
                    console.warn('清空过程中的警告:', result.warnings);
                    showToast(`清空完成，但有 ${result.warnings.length} 个警告，请查看控制台`, 'warning');
                }
            } else {
                throw new Error(result.error || '清空失败');
            }
        } catch (error) {
            console.error('清空产出物失败:', error);
            showToast('清空产出物失败: ' + error.message, 'error');
        }
    },
    
    // 更新显示
    updateDisplay() {
        const outputsList = document.getElementById('outputsList');
        if (!outputsList) return;
        
        if (this.outputs.length === 0) {
            outputsList.innerHTML = '<p class="no-outputs">暂无产出物</p>';
            return;
        }
        
        outputsList.innerHTML = this.outputs.map(output => `
            <div class="output-item">
                <div class="output-header">
                    <div class="output-info">
                        <div class="output-filename">${output.filename}</div>
                        <div class="output-meta">
                            <span><i class="fas fa-calendar"></i> ${new Date(output.createdAt).toLocaleString()}</span>
                            <span><i class="fas fa-hdd"></i> ${this.formatFileSize(output.fileSize)}</span>
                            ${output.duration ? `<span><i class="fas fa-clock"></i> ${this.formatDuration(output.duration)}</span>` : ''}
                        </div>
                    </div>
                    <div class="output-actions">
                        <button class="btn btn-info" onclick="outputManager.showOutputDetail(${output.id})" title="查看详情">
                            <i class="fas fa-info"></i>
                        </button>
                        <button class="btn btn-download" onclick="outputManager.downloadOutput(${output.id})" title="下载">
                            <i class="fas fa-download"></i>
                        </button>
                        <button class="btn btn-delete" onclick="outputManager.deleteOutput(${output.id})" title="删除">
                            <i class="fas fa-trash"></i>
                        </button>
                    </div>
                </div>
                ${this.renderOperations(output.operations)}
            </div>
        `).join('');
    },
    
    // 渲染操作标签
    renderOperations(operations) {
        if (!operations || operations.length === 0) {
            return '';
        }
        
        return `
            <div class="output-operations">
                <div class="output-operations-title">
                    <i class="fas fa-cogs"></i> 处理操作
                </div>
                <div class="operations-list">
                    ${operations.map(op => `
                        <span class="operation-tag">
                            <i class="fas fa-play"></i>
                            ${this.getOperationName(op.type)}
                        </span>
                    `).join('')}
                </div>
            </div>
        `;
    },
    
    // 保存到本地存储
    saveOutputs() {
        try {
            localStorage.setItem('videoProcessingOutputs', JSON.stringify(this.outputs));
        } catch (error) {
            console.error('保存产出物数据失败:', error);
        }
    },
    
    // 从本地存储加载
    loadOutputs() {
        try {
            const saved = localStorage.getItem('videoProcessingOutputs');
            if (saved) {
                this.outputs = JSON.parse(saved);
            }
        } catch (error) {
            console.error('加载产出物数据失败:', error);
            this.outputs = [];
        }
    }
};

// 修改视频处理完成的处理函数，添加产出物记录
const originalCompleteProcessing = videoProcessing.completeProcessing;
videoProcessing.completeProcessing = function(data) {
    // 调用原始的完成处理函数
    originalCompleteProcessing.call(this, data);
    
    // 添加产出物记录
    if (data.outputPath && data.workflow) {
        const filename = data.outputPath.split(/[/\\]/).pop();
        const originalFile = data.workflow.originalFile || '未知';
        
        outputManager.addOutput({
            filename: filename,
            filepath: data.outputPath,
            originalFile: originalFile,
            operations: data.workflow.operations || [],
            fileSize: data.fileSize || 0,
            duration: data.duration || 0
        });
    }
};



// 合并文件管理器
const mergeFileManager = {
    selectedFiles: [],
    
    // 显示文件选择器
    showFileSelector() {
        console.log('mergeFileManager.showFileSelector() 被调用');
        // 使用与其他处理方式相同的文件选择器
        const availableFiles = videoProcessing.getAvailableVideoFiles();
        console.log('可用文件:', availableFiles);
        
        if (availableFiles.length === 0) {
            showToast('暂无可处理的视频文件，请先下载一些视频文件', 'warning');
            return;
        }
        
        // 检查是否为串行处理模式和是否为第一个操作
        const serialProcessing = document.getElementById('serialProcessing')?.checked || false;
        const isFirstOperation = videoProcessing.workflow.length === 0;
        
        // 创建模态框
        const modal = document.createElement('div');
        modal.className = 'modal fade';
        modal.id = 'mergeFileSelectModal';
        
        // 根据串行模式调整提示信息
        let infoMessage = '请选择至少2个视频文件进行合并。文件将按照您选择的顺序进行合并。';
        if (serialProcessing && !isFirstOperation) {
            infoMessage = '串行模式：建议选择"使用前一步输出"，然后再选择至少一个其他文件进行合并。';
        }
        
        modal.innerHTML = `
            <div class="modal-dialog modal-lg">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">
                            <i class="fas fa-layer-group"></i>
                            选择要合并的视频文件
                        </h5>
                        <button type="button" class="btn-close" onclick="mergeFileManager.closeMergeFileSelector()"></button>
                    </div>
                    <div class="modal-body">
                        <div class="merge-file-selector-info">
                            <div class="alert alert-info">
                                <i class="fas fa-info-circle"></i>
                                ${infoMessage}
                            </div>
                        </div>
                        
                        ${videoProcessing.generateFileSelector(availableFiles, serialProcessing, isFirstOperation)}
                        
                        <div class="selected-merge-files-preview">
                            <h6>已选择的文件 (<span id="selectedCount">0</span>):</h6>
                            <div id="selectedMergePreview" class="selected-files-list">
                                <div class="no-selection">尚未选择任何文件</div>
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" onclick="mergeFileManager.cancelMergeFileSelection()">取消</button>
                        <button type="button" class="btn btn-primary" onclick="mergeFileManager.confirmMergeSelection()" id="confirmMergeBtn" disabled>
                            确认选择 (<span id="confirmCount">0</span>)
                        </button>
                    </div>
                </div>
            </div>
        `;
        
        // 添加到页面
        document.body.appendChild(modal);
        
        // 显示模态框
        modal.style.display = 'flex';
        modal.style.position = 'fixed';
        modal.style.top = '0';
        modal.style.left = '0';
        modal.style.width = '100%';
        modal.style.height = '100%';
        modal.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
        modal.style.zIndex = '10000';
        modal.style.alignItems = 'center';
        modal.style.justifyContent = 'center';
        modal.classList.add('show');
        
        console.log('模态框已创建并显示:', modal);
        
        // 初始化文件选择器
        setTimeout(() => {
            const selectorId = modal.querySelector('.file-selector').id;
            // 只初始化合并文件选择器，不调用普通的文件选择器初始化
            // videoProcessing.initFileSelector(selectorId); // 注释掉这行，避免冲突
            this.initMergeFileSelector(selectorId);
        }, 100);
    },

    // 初始化合并文件选择器
    initMergeFileSelector(selectorId) {
        const selector = document.getElementById(selectorId);
        if (!selector) return;

        // 初始化视图模式按钮（从videoProcessing.initFileSelector复制）
        videoProcessing.initViewModeButtons(selectorId);

        // 修改文件选择器为多选模式
        const fileOptions = selector.querySelectorAll('.file-option');
        fileOptions.forEach(option => {
            const filename = option.dataset.filename;
            
            // 清除可能存在的旧事件监听器
            option.replaceWith(option.cloneNode(true));
        });
        
        // 重新获取文件选项（因为上面替换了节点）
        const newFileOptions = selector.querySelectorAll('.file-option');
        newFileOptions.forEach(option => {
            const filename = option.dataset.filename;
            
            // 设置初始选中状态
            const isSelected = this.selectedFiles.includes(filename);
            if (isSelected) {
                option.classList.add('selected');
            }
            
            // 添加点击事件监听器
            option.addEventListener('click', (e) => {
                e.preventDefault();
                
                // 处理"使用前一步输出"选项
                if (filename === '使用前一步输出') {
                    this.toggleMergeFile('__PREVIOUS_OUTPUT__');
                } else if (filename) {
                    this.toggleMergeFile(filename);
                }
                
                // 更新所有文件选项的状态，而不是只更新当前点击的文件
                this.updateAllMergeFileOptions();
                this.updateMergePreview();
            });
        });
        
        // 初始化预览显示
        this.updateMergePreview();
    },

    // 切换合并文件选择状态
    toggleMergeFile(filename) {
        const index = this.selectedFiles.indexOf(filename);
        if (index > -1) {
            this.selectedFiles.splice(index, 1);
        } else {
            this.selectedFiles.push(filename);
        }
    },

    // 更新文件选项显示状态
    updateMergeFileOption(option, filename) {
        const isSelected = this.selectedFiles.includes(filename);
        if (isSelected) {
            option.classList.add('selected');
        } else {
            option.classList.remove('selected');
        }
    },

    // 更新所有文件选项的选中状态
    updateAllMergeFileOptions() {
        const modal = document.getElementById('mergeFileSelectModal');
        if (!modal) return;

        const fileOptions = modal.querySelectorAll('.file-option');
        fileOptions.forEach(option => {
            const filename = option.dataset.filename;
            let isSelected = false;
            
            if (filename === '使用前一步输出') {
                // 检查是否选择了前一步输出
                isSelected = this.selectedFiles.includes('__PREVIOUS_OUTPUT__');
            } else if (filename) {
                isSelected = this.selectedFiles.includes(filename);
            }
            
            if (isSelected) {
                option.classList.add('selected');
            } else {
                option.classList.remove('selected');
            }
        });
    },

    // 更新合并预览
    updateMergePreview() {
        const selectedCountEl = document.getElementById('selectedCount');
        const confirmCountEl = document.getElementById('confirmCount');
        const previewEl = document.getElementById('selectedMergePreview');
        const confirmBtn = document.getElementById('confirmMergeBtn');

        if (selectedCountEl) selectedCountEl.textContent = this.selectedFiles.length;
        if (confirmCountEl) confirmCountEl.textContent = this.selectedFiles.length;

        if (previewEl) {
            if (this.selectedFiles.length === 0) {
                previewEl.innerHTML = '<div class="no-selection">尚未选择任何文件</div>';
            } else {
                previewEl.innerHTML = this.selectedFiles.map((filename, index) => {
                    // 显示友好的文件名
                    const displayName = filename === '__PREVIOUS_OUTPUT__' ? '使用前一步输出' : filename;
                    const removeAction = filename === '__PREVIOUS_OUTPUT__' ? 
                        `mergeFileManager.removeMergeFile('__PREVIOUS_OUTPUT__')` : 
                        `mergeFileManager.removeMergeFile('${filename}')`;
                    
                    return `
                        <div class="selected-file-preview">
                            <span class="file-order">${index + 1}</span>
                            <span class="file-name">${displayName}</span>
                            <button type="button" class="btn btn-sm btn-outline-danger" onclick="${removeAction}">
                                <i class="fas fa-times"></i>
                            </button>
                        </div>
                    `;
                }).join('');
            }
        }

        if (confirmBtn) {
            confirmBtn.disabled = this.selectedFiles.length < 2;
        }
    },

    // 移除合并文件
    removeMergeFile(filename) {
        const index = this.selectedFiles.indexOf(filename);
        if (index > -1) {
            this.selectedFiles.splice(index, 1);
            this.updateMergePreview();
            
            // 更新所有文件选项的显示状态
            this.updateAllMergeFileOptions();
        }
    },

    // 关闭合并文件选择器
    closeMergeFileSelector() {
        const modal = document.getElementById('mergeFileSelectModal');
        if (modal) {
            modal.remove();
        }
        // 注意：这里不重置selectedFiles，因为用户可能只是想关闭窗口而不是取消选择
    },

    // 取消合并文件选择（重置选择）
    cancelMergeFileSelection() {
        // 重置已选择的文件列表
        this.selectedFiles = [];
        
        // 关闭模态框
        this.closeMergeFileSelector();
        
        // 更新主界面的显示
        this.updateSelectedFilesDisplay();
        
        console.log('已重置合并文件选择');
    },

    // 确认合并选择
    confirmMergeSelection() {
        if (this.selectedFiles.length >= 2) {
            this.closeMergeFileSelector();
            this.updateSelectedFilesDisplay();
            showToast(`已选择 ${this.selectedFiles.length} 个文件进行合并`, 'success');
        } else {
            showToast('请选择至少2个文件进行合并', 'warning');
        }
    },

    // 加载文件列表（保留原有方法以兼容）
    async loadFileList() {
        try {
            const response = await fetch('/api/files');
            const data = await response.json();
            
            // 从API响应中提取文件数组
            const files = data.files || [];
            
            const fileList = document.getElementById('fileSelectList');
            if (!fileList) return;
            
            if (files.length === 0) {
                fileList.innerHTML = '<p class="no-files">暂无文件</p>';
                return;
            }
            
            fileList.innerHTML = files.map(file => {
                const isSelected = this.selectedFiles.includes(file.name);
                const fileType = this.getFileType(file.name);
                
                return `
                    <div class="file-select-item ${isSelected ? 'selected' : ''}" onclick="mergeFileManager.toggleFile('${file.name}')">
                        <input type="checkbox" ${isSelected ? 'checked' : ''} onchange="mergeFileManager.toggleFile('${file.name}')">
                        <i class="file-item-icon ${fileType}"></i>
                        <div class="file-item-info">
                            <div class="file-item-name">${file.name}</div>
                            <div class="file-item-details">
                                <span>${this.formatFileSize(file.size)}</span>
                                ${file.duration ? `<span>${this.formatDuration(file.duration)}</span>` : ''}
                                ${file.isVideo && (file.bitrate || file.orientation || file.width || file.height) ? 
                                    (() => {
                                        const bitrateText = file.bitrate ? `${formatBitrate(file.bitrate)}` : '';
                                        const orientationText = file.orientation ? `${formatOrientation(file.orientation)}` : '';
                                        const resolutionText = (file.width && file.height) ? `${formatResolution(file.width, file.height)}` : '';
                                        const videoDetails = [bitrateText, orientationText, resolutionText].filter(Boolean);
                                        return videoDetails.length > 0 ? videoDetails.map(detail => `<span>${detail}</span>`).join('') : '';
                                    })() : ''
                                }
                            </div>
                        </div>
                    </div>
                `;
            }).join('');
        } catch (error) {
            console.error('加载文件列表失败:', error);
            const fileList = document.getElementById('fileSelectList');
            if (fileList) {
                fileList.innerHTML = '<p class="no-files">加载文件失败</p>';
            }
            showToast('加载文件列表失败', 'error');
        }
    },
    
    // 切换文件选择状态
    toggleFile(filename) {
        const index = this.selectedFiles.indexOf(filename);
        if (index > -1) {
            this.selectedFiles.splice(index, 1);
        } else {
            this.selectedFiles.push(filename);
        }
        
        // 更新文件列表显示
        this.loadFileList();
        
        // 更新已选择文件显示
        this.updateMergeFilesDisplay();
        
        // 更新确定按钮状态
        this.updateConfirmButton();
    },
    
    // 更新合并文件显示
    updateMergeFilesDisplay() {
        const container = document.getElementById('selectedMergeFiles');
        const countElement = document.getElementById('mergeFileCount');
        
        if (!container) return;
        
        // 更新文件计数
        if (countElement) {
            countElement.textContent = this.selectedFiles.length;
        }
        
        if (this.selectedFiles.length === 0) {
            container.innerHTML = `
                <div class="merge-files-placeholder">
                    请从上方列表中选择要合并的文件
                </div>
            `;
        } else {
            container.innerHTML = this.selectedFiles.map((filename, index) => `
                <div class="selected-file-item">
                    <div class="file-order">${index + 1}</div>
                    <div class="file-info">
                        <i class="fas fa-file-video"></i>
                        <span class="file-name">${filename}</span>
                    </div>
                    <div class="file-actions">
                        <button type="button" class="btn btn-sm btn-outline-secondary" onclick="mergeFileManager.moveFileUp(${index})" ${index === 0 ? 'disabled' : ''}>
                            <i class="fas fa-arrow-up"></i>
                        </button>
                        <button type="button" class="btn btn-sm btn-outline-secondary" onclick="mergeFileManager.moveFileDown(${index})" ${index === this.selectedFiles.length - 1 ? 'disabled' : ''}>
                            <i class="fas fa-arrow-down"></i>
                        </button>
                        <button type="button" class="btn btn-sm btn-outline-danger" onclick="mergeFileManager.removeFile(${index})">
                            <i class="fas fa-times"></i>
                        </button>
                    </div>
                </div>
            `).join('');
        }
    },
    
    // 更新确定按钮状态
    updateConfirmButton() {
        const confirmBtn = document.getElementById('fileSelectConfirmBtn');
        if (confirmBtn) {
            confirmBtn.disabled = this.selectedFiles.length < 2;
        }
    },
    
    // 关闭文件选择器
    closeFileSelector() {
        const modal = document.getElementById('fileSelectModal');
        const mergeContainer = document.getElementById('mergeFilesContainer');
        
        if (modal) {
            modal.style.display = 'none';
        }
        
        if (mergeContainer) {
            mergeContainer.style.display = 'none';
        }
        
        // 更新主界面的已选择文件显示
        this.updateSelectedFilesDisplay();
    },
    
    // 确认选择
    confirmSelection() {
        if (this.selectedFiles.length >= 2) {
            this.closeFileSelector();
            showToast(`已选择 ${this.selectedFiles.length} 个文件进行合并`, 'success');
        } else {
            showToast('请选择至少2个文件进行合并', 'warning');
        }
    },
    
    // 获取文件类型
    getFileType(filename) {
        const ext = filename.toLowerCase().split('.').pop();
        if (['mp4', 'avi', 'mov', 'mkv', 'wmv', 'flv', 'webm'].includes(ext)) {
            return 'video';
        } else if (['mp3', 'wav', 'aac', 'flac', 'ogg'].includes(ext)) {
            return 'audio';
        } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)) {
            return 'image';
        }
        return 'other';
    },
    
    // 更新已选择文件显示（主界面）
    updateSelectedFilesDisplay() {
        const container = document.getElementById('selectedMergeFiles');
        if (!container) return;
        
        if (this.selectedFiles.length === 0) {
            container.innerHTML = `
                <div class="merge-files-placeholder">
                    <i class="fas fa-plus-circle"></i>
                    <span>点击下方"添加文件"按钮选择要合并的文件</span>
                </div>
            `;
        } else {
            container.innerHTML = this.selectedFiles.map((filename, index) => {
                // 显示友好的文件名
                const displayName = filename === '__PREVIOUS_OUTPUT__' ? '使用前一步输出' : filename;
                const removeAction = filename === '__PREVIOUS_OUTPUT__' ? 
                    `mergeFileManager.removeFile(${index})` : 
                    `mergeFileManager.removeFile(${index})`;
                
                return `
                    <div class="selected-file-item">
                        <div class="file-order">${index + 1}</div>
                        <div class="file-info">
                            <i class="fas fa-file-video"></i>
                            <span class="file-name">${displayName}</span>
                        </div>
                        <div class="file-actions">
                            <button type="button" class="btn btn-sm btn-outline-secondary" onclick="mergeFileManager.moveFileUp(${index})" ${index === 0 ? 'disabled' : ''}>
                                <i class="fas fa-arrow-up"></i>
                            </button>
                            <button type="button" class="btn btn-sm btn-outline-secondary" onclick="mergeFileManager.moveFileDown(${index})" ${index === this.selectedFiles.length - 1 ? 'disabled' : ''}>
                                <i class="fas fa-arrow-down"></i>
                            </button>
                            <button type="button" class="btn btn-sm btn-outline-danger" onclick="${removeAction}">
                                <i class="fas fa-times"></i>
                            </button>
                        </div>
                    </div>
                `;
            }).join('');
        }
    },
    
    // 移除文件
    removeFile(index) {
        this.selectedFiles.splice(index, 1);
        this.updateSelectedFilesDisplay();
    },
    
    // 上移文件
    moveFileUp(index) {
        if (index > 0) {
            [this.selectedFiles[index], this.selectedFiles[index - 1]] = 
            [this.selectedFiles[index - 1], this.selectedFiles[index]];
            this.updateSelectedFilesDisplay();
        }
    },
    
    // 下移文件
    moveFileDown(index) {
        if (index < this.selectedFiles.length - 1) {
            [this.selectedFiles[index], this.selectedFiles[index + 1]] = 
            [this.selectedFiles[index + 1], this.selectedFiles[index]];
            this.updateSelectedFilesDisplay();
        }
    },
    
    // 清空文件
    clearFiles() {
        this.selectedFiles = [];
        this.updateSelectedFilesDisplay();
    },
    
    // 获取选中的文件
    getSelectedFiles() {
        return this.selectedFiles;
    },
    
    // 格式化文件大小
    formatFileSize(bytes) {
        if (!bytes) return '未知';
        const sizes = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(1024));
        return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
    },
    
    // 格式化时长
    formatDuration(seconds) {
        if (!seconds) return '未知';
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    }
};

// 为videoProcessing对象添加合并文件相关方法
videoProcessing.showMergeFileSelector = function() {
    console.log('videoProcessing.showMergeFileSelector() 被调用');
    mergeFileManager.showFileSelector();
};

videoProcessing.clearMergeFiles = function() {
    mergeFileManager.clearFiles();
};

// 页面加载完成后测试方法是否正确定义
document.addEventListener('DOMContentLoaded', function() {
    console.log('页面加载完成，测试方法定义:');
    console.log('videoProcessing.showMergeFileSelector:', typeof videoProcessing.showMergeFileSelector);
    console.log('mergeFileManager.showFileSelector:', typeof mergeFileManager.showFileSelector);
});

// 全局文件选择模态框函数
function closeFileSelectModal() {
    const modal = document.getElementById('fileSelectModal');
    const mergeContainer = document.getElementById('mergeFilesContainer');
    
    if (modal) {
        modal.style.display = 'none';
    }
    
    if (mergeContainer) {
        mergeContainer.style.display = 'none';
    }
}

function filterFiles() {
    const searchInput = document.getElementById('fileSearchInput');
    const typeFilter = document.getElementById('fileTypeFilter');
    const fileList = document.getElementById('fileSelectList');
    
    if (!searchInput || !fileList) return;
    
    const searchTerm = searchInput.value.toLowerCase();
    const selectedType = typeFilter ? typeFilter.value : 'all';
    
    const fileItems = fileList.querySelectorAll('.file-select-item');
    
    fileItems.forEach(item => {
        const fileName = item.querySelector('.file-item-name');
        const fileIcon = item.querySelector('.file-item-icon');
        
        if (!fileName) return;
        
        const nameMatch = fileName.textContent.toLowerCase().includes(searchTerm);
        const typeMatch = selectedType === 'all' || 
                         (fileIcon && fileIcon.classList.contains(selectedType));
        
        item.style.display = (nameMatch && typeMatch) ? 'flex' : 'none';
    });
}

function sortFiles() {
    const sortSelect = document.getElementById('fileSortSelect');
    const fileList = document.getElementById('fileSelectList');
    
    if (!sortSelect || !fileList) return;
    
    const sortBy = sortSelect.value;
    const fileItems = Array.from(fileList.querySelectorAll('.file-select-item'));
    
    fileItems.sort((a, b) => {
        const aName = a.querySelector('.file-item-name')?.textContent || '';
        const bName = b.querySelector('.file-item-name')?.textContent || '';
        
        switch (sortBy) {
            case 'name':
                return aName.localeCompare(bName);
            case 'size':
                // 这里可以添加按大小排序的逻辑
                return aName.localeCompare(bName);
            case 'date':
                // 这里可以添加按日期排序的逻辑
                return aName.localeCompare(bName);
            case 'type':
                const aType = a.querySelector('.file-item-icon')?.className || '';
                const bType = b.querySelector('.file-item-icon')?.className || '';
                return aType.localeCompare(bType);
            default:
                return 0;
        }
    });
    
    // 重新排列DOM元素
    fileItems.forEach(item => fileList.appendChild(item));
}

// 删除下载文件功能
async function deleteDownloadFile(event, filepath) {
    // 找到触发删除的按钮并禁用
    const deleteButton = event.target.closest('.delete-file-btn');
    if (deleteButton) {
        deleteButton.disabled = true;
        deleteButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
    }
    
    // 保存文件管理操作状态
    if (typeof globalStateManager !== 'undefined') {
        globalStateManager.updateModuleState('fileManagement', {
            currentOperation: `正在删除文件: ${filepath}`,
            operationInProgress: true
        });
    }
    
    try {
        // 显示确认对话框
        const confirmed = await showCustomConfirm(
            '确认删除文件',
            `确定要删除文件 "${filepath}" 吗？此操作不可撤销。`
        );
        
        if (!confirmed) {
            // 用户取消，恢复按钮状态
            if (deleteButton) {
                deleteButton.disabled = false;
                deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
            }
            // 清除操作状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('fileManagement', {
                    currentOperation: null,
                    operationInProgress: false
                });
            }
            return;
        }
        
        // 发送删除请求
        const response = await fetch('/api/delete-download-file', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ filepath })
        });
        
        const result = await response.json();
        
        if (result.success) {
            showToast('文件删除成功', 'success');
            
            // 保存删除成功状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('fileManagement', {
                    currentOperation: `文件删除成功: ${filepath}`,
                    operationInProgress: false,
                    lastOperation: {
                        type: 'delete_file',
                        target: filepath,
                        timestamp: new Date().toISOString(),
                        success: true
                    }
                });
            }
            
            // 直接移除DOM元素而不是重新加载整个列表
            const downloadItem = deleteButton.closest('.download-item');
            if (downloadItem) {
                // 添加淡出动画
                downloadItem.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out';
                downloadItem.style.opacity = '0';
                downloadItem.style.transform = 'translateX(-20px)';
                
                // 动画完成后移除元素
                setTimeout(() => {
                    downloadItem.remove();
                    
                    // 检查文件夹是否还有其他文件
                    const folderContent = downloadItem.closest('.folder-content');
                    if (folderContent) {
                        const remainingFiles = folderContent.querySelectorAll('.download-item');
                        if (remainingFiles.length === 0) {
                            // 如果文件夹下没有文件了，移除整个文件夹组
                            const folderGroup = folderContent.closest('.folder-group');
                            if (folderGroup) {
                                folderGroup.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out';
                                folderGroup.style.opacity = '0';
                                folderGroup.style.transform = 'translateX(-20px)';
                                setTimeout(() => {
                                    folderGroup.remove();
                                    
                                    // 检查日期组是否还有其他文件夹
                                    const dateGroup = folderGroup.closest('.date-group');
                                    if (dateGroup) {
                                        const remainingFolders = dateGroup.querySelectorAll('.folder-group');
                                        if (remainingFolders.length === 0) {
                                            // 如果这个日期组下没有文件夹了，也移除日期组
                                            dateGroup.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out';
                                            dateGroup.style.opacity = '0';
                                            dateGroup.style.transform = 'translateX(-20px)';
                                            setTimeout(() => {
                                                dateGroup.remove();
                                            }, 300);
                                        }
                                    }
                                }, 300);
                            }
                        } else {
                            // 更新文件夹标题中的文件数量
                            const folderGroup = folderContent.closest('.folder-group');
                            if (folderGroup) {
                                const folderHeader = folderGroup.querySelector('.folder-header span');
                                if (folderHeader) {
                                    const folderName = folderHeader.textContent.split(' (')[0];
                                    folderHeader.textContent = `${folderName} (${remainingFiles.length} 个文件)`;
                                }
                            }
                        }
                    }
                }, 300);
            }
        } else {
            showToast('删除失败: ' + result.error, 'error');
            // 保存删除失败状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('fileManagement', {
                    currentOperation: `文件删除失败: ${result.error}`,
                    operationInProgress: false,
                    lastOperation: {
                        type: 'delete_file',
                        target: filepath,
                        timestamp: new Date().toISOString(),
                        success: false,
                        error: result.error
                    }
                });
            }
            // 恢复按钮状态
            if (deleteButton) {
                deleteButton.disabled = false;
                deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
            }
        }
    } catch (error) {
        console.error('删除文件失败:', error);
        showToast('删除文件失败: ' + error.message, 'error');
        // 保存删除错误状态
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.updateModuleState('fileManagement', {
                currentOperation: `文件删除错误: ${error.message}`,
                operationInProgress: false,
                lastOperation: {
                    type: 'delete_file',
                    target: filepath,
                    timestamp: new Date().toISOString(),
                    success: false,
                    error: error.message
                }
            });
        }
        // 恢复按钮状态
        if (deleteButton) {
            deleteButton.disabled = false;
            deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
        }
    }
}

// 删除下载文件夹功能
async function deleteDownloadFolder(event, folderpath) {
    // 找到触发删除的按钮并禁用
    const deleteButton = event.target.closest('.delete-folder-btn');
    if (deleteButton) {
        deleteButton.disabled = true;
        deleteButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 删除中...';
    }
    
    // 保存删除操作开始状态
    if (typeof globalStateManager !== 'undefined') {
        globalStateManager.updateModuleState('fileManagement', {
            currentOperation: `正在删除文件夹: ${folderpath}`,
            operationInProgress: true,
            lastOperation: {
                type: 'delete_folder',
                target: folderpath,
                timestamp: new Date().toISOString(),
                status: 'in_progress'
            }
        });
    }
    
    try {
        // 显示确认对话框
        const confirmed = await showCustomConfirm(
            '确认删除文件夹',
            `确定要删除文件夹 "${folderpath}" 及其所有内容吗？此操作不可撤销。`
        );
        
        if (!confirmed) {
            // 用户取消，恢复按钮状态
            if (deleteButton) {
                deleteButton.disabled = false;
                deleteButton.innerHTML = '<i class="fas fa-folder-minus"></i> 删除文件夹';
            }
            // 保存取消状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('fileManagement', {
                    currentOperation: `文件夹删除已取消: ${folderpath}`,
                    operationInProgress: false,
                    lastOperation: {
                        type: 'delete_folder',
                        target: folderpath,
                        timestamp: new Date().toISOString(),
                        status: 'cancelled'
                    }
                });
            }
            return;
        }
        
        // 发送删除请求
        const response = await fetch('/api/delete-download-folder', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ folderpath })
        });
        
        const result = await response.json();
        
        if (result.success) {
            showToast('文件夹删除成功', 'success');
            
            // 保存删除成功状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('fileManagement', {
                    currentOperation: `文件夹删除成功: ${folderpath}`,
                    operationInProgress: false,
                    lastOperation: {
                        type: 'delete_folder',
                        target: folderpath,
                        timestamp: new Date().toISOString(),
                        success: true
                    }
                });
            }
            
            // 直接移除DOM元素而不是重新加载整个列表
            const folderGroup = deleteButton.closest('.folder-group');
            if (folderGroup) {
                // 添加淡出动画
                folderGroup.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out';
                folderGroup.style.opacity = '0';
                folderGroup.style.transform = 'translateX(-20px)';
                
                // 动画完成后移除元素
                setTimeout(() => {
                    folderGroup.remove();
                    
                    // 检查是否还有其他文件夹，如果当前日期组下没有文件夹了，也移除日期组
                    const dateGroup = folderGroup.closest('.date-group');
                    if (dateGroup) {
                        const remainingFolders = dateGroup.querySelectorAll('.folder-group');
                        if (remainingFolders.length === 0) {
                            // 如果这个日期组下没有文件夹了，也移除日期组
                            dateGroup.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out';
                            dateGroup.style.opacity = '0';
                            dateGroup.style.transform = 'translateX(-20px)';
                            setTimeout(() => {
                                dateGroup.remove();
                            }, 300);
                        }
                    }
                }, 300);
            }
        } else {
            showToast('删除失败: ' + result.error, 'error');
            // 保存删除失败状态
            if (typeof globalStateManager !== 'undefined') {
                globalStateManager.updateModuleState('fileManagement', {
                    currentOperation: `文件夹删除失败: ${result.error}`,
                    operationInProgress: false,
                    lastOperation: {
                        type: 'delete_folder',
                        target: folderpath,
                        timestamp: new Date().toISOString(),
                        success: false,
                        error: result.error
                    }
                });
            }
            // 恢复按钮状态
            if (deleteButton) {
                deleteButton.disabled = false;
                deleteButton.innerHTML = '<i class="fas fa-folder-minus"></i> 删除文件夹';
            }
        }
    } catch (error) {
        console.error('删除文件夹失败:', error);
        showToast('删除文件夹失败: ' + error.message, 'error');
        // 保存删除错误状态
        if (typeof globalStateManager !== 'undefined') {
            globalStateManager.updateModuleState('fileManagement', {
                currentOperation: `文件夹删除错误: ${error.message}`,
                operationInProgress: false,
                lastOperation: {
                    type: 'delete_folder',
                    target: folderpath,
                    timestamp: new Date().toISOString(),
                    success: false,
                    error: error.message
                }
            });
        }
        // 恢复按钮状态
        if (deleteButton) {
            deleteButton.disabled = false;
            deleteButton.innerHTML = '<i class="fas fa-folder-minus"></i> 删除文件夹';
        }
    }
}